Skip to content

appwrite/sdk-for-react

Appwrite React SDK

Discord

Appwrite is an open-source backend as a service that abstracts common application features behind simple APIs. The Appwrite React SDK provides React hooks and framework adapters for Appwrite authentication in client-rendered and server-rendered React apps.

Appwrite

Features

  • React provider and hooks for sign-up, sign-in, sign-out, OAuth, and current user state
  • Client-side auth for Vite and other non-SSR React apps
  • SSR auth handlers for Next.js and TanStack Start
  • Server helpers for reading the current user/session from HTTP-only cookies
  • Server-context session clients and admin clients

Installation

For client-rendered React apps:

pnpm add @appwrite.io/react appwrite @tanstack/react-query

For SSR apps that use the server handlers or admin client, also install node-appwrite:

pnpm add @appwrite.io/react appwrite node-appwrite @tanstack/react-query

Framework packages such as next, @tanstack/react-start, react, and react-dom should come from your app scaffold.

Non-SSR React Apps

Use this setup for Vite or any app where auth is handled directly in the browser.

import { AppwriteProvider } from "@appwrite.io/react";

export function Root() {
  return (
    <AppwriteProvider
      endpoint={import.meta.env.VITE_APPWRITE_ENDPOINT}
      projectId={import.meta.env.VITE_APPWRITE_PROJECT_ID}
    >
      <App />
    </AppwriteProvider>
  );
}

Then, for authentication:

import { useAuth } from "@appwrite.io/react";

export function AuthPanel() {
  const { user, isLoading, signIn, signUp, signOut } = useAuth();

  if (isLoading) return <p>Loading...</p>;

  if (!user) {
    return (
      <>
        <button
          onClick={() =>
            signIn.emailPassword({
              email: "user@example.com",
              password: "password123",
            })
          }
        >
          Sign in
        </button>
        <button
          onClick={() =>
            signUp.emailPassword({
              email: "user@example.com",
              password: "password123",
              name: "Jane Doe",
            })
          }
        >
          Sign up
        </button>
      </>
    );
  }

  return (
    <>
      <p>Signed in as {user.email}</p>
      <button onClick={() => signOut.signOut()}>Sign out</button>
    </>
  );
}

Next.js App Router SSR

Set public Appwrite values and keep only the API key private:

NEXT_PUBLIC_APPWRITE_ENDPOINT=https://fra.cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID=PROJECT_ID
APPWRITE_API_KEY=SERVER_ONLY_API_KEY

Create the Appwrite auth handler route:

// app/api/appwrite/[...appwrite]/route.ts
import { createAppwriteHandlers } from "@appwrite.io/react/handlers/next";

export const { GET, POST } = createAppwriteHandlers({
  endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
  projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
  apiKey: process.env.APPWRITE_API_KEY!,
  basePath: "/api/appwrite",
});

Pass the server session into the client provider:

// app/providers.tsx
"use client";

import { AppwriteProvider } from "@appwrite.io/react";

export function Providers({
  session,
  children,
}: {
  session?: string | null;
  children: React.ReactNode;
}) {
  return (
    <AppwriteProvider
      endpoint={process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!}
      projectId={process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!}
      ssr={{ session, basePath: "/api/appwrite" }}
    >
      {children}
    </AppwriteProvider>
  );
}
// app/layout.tsx
import { createNextServerHelpers } from "@appwrite.io/react/server/next";
import { Providers } from "./providers";

const appwrite = {
  endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
  projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const helpers = createNextServerHelpers(appwrite);
  const session = await helpers.readSessionCookie();

  return (
    <html lang="en">
      <body>
        <Providers session={session}>{children}</Providers>
      </body>
    </html>
  );
}

Read SSR auth state and create server-context clients:

// app/page.tsx
import { createNextServerHelpers } from "@appwrite.io/react/server/next";

const appwrite = {
  endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
  projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
  apiKey: process.env.APPWRITE_API_KEY!,
};

export default async function Page() {
  const helpers = createNextServerHelpers(appwrite);

  const user = await helpers.getLoggedInUser();
  const sessionClient = await helpers.createSessionClient();
  const adminClient = helpers.createAdminClient();
  const users = await adminClient.users.list({ total: false });

  return (
    <main>
      <p>SSR user: {user?.email ?? "signed out"}</p>
      <p>Session client: {sessionClient ? "available" : "none"}</p>
      <p>Admin users loaded: {users.users.length}</p>
    </main>
  );
}

In client components, use the same hooks. Refresh the router after auth mutations when server-rendered state should update.

// app/auth-panel.tsx
"use client";

import { useAuth } from "@appwrite.io/react";
import { useRouter } from "next/navigation";

export function AuthPanel() {
  const { user, isLoading, signIn, signOut } = useAuth();
  const router = useRouter();

  if (isLoading) return <p>Loading...</p>;

  if (!user) {
    return (
      <button
        onClick={() =>
          signIn.emailPassword({
            email: "user@example.com",
            password: "password123",
            onSuccess: () => router.refresh(),
          })
        }
      >
        Sign in
      </button>
    );
  }

  return (
    <button
      onClick={() => signOut.signOut({ onSuccess: () => router.refresh() })}
    >
      Sign out
    </button>
  );
}

TanStack Start SSR

Set public Appwrite values and keep only the API key private:

VITE_APPWRITE_ENDPOINT=https://fra.cloud.appwrite.io/v1
VITE_APPWRITE_PROJECT_ID=PROJECT_ID
APPWRITE_API_KEY=SERVER_ONLY_API_KEY

Create the Appwrite auth handler route:

// src/routes/api/appwrite/$.ts
import { createFileRoute } from "@tanstack/react-router";
import { createAppwriteHandlers } from "@appwrite.io/react/handlers/tanstack";

export const Route = createFileRoute("/api/appwrite/$")({
  server: {
    handlers: createAppwriteHandlers({
      endpoint: import.meta.env.VITE_APPWRITE_ENDPOINT,
      projectId: import.meta.env.VITE_APPWRITE_PROJECT_ID,
      apiKey: process.env.APPWRITE_API_KEY!,
      basePath: "/api/appwrite",
    }),
  },
});

Read SSR auth state in a server function and pass the session into the provider:

// src/routes/index.tsx
import { createFileRoute, useRouter } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { AppwriteProvider, useAuth } from "@appwrite.io/react";
import { createTanStackServerHelpers } from "@appwrite.io/react/server/tanstack";

const getAuthSnapshot = createServerFn({ method: "GET" }).handler(async () => {
  const appwrite = {
    endpoint: import.meta.env.VITE_APPWRITE_ENDPOINT,
    projectId: import.meta.env.VITE_APPWRITE_PROJECT_ID,
    apiKey: process.env.APPWRITE_API_KEY!,
  };

  const helpers = createTanStackServerHelpers(appwrite);
  const user = await helpers.getLoggedInUser();
  const sessionClient = await helpers.createSessionClient();
  const adminClient = helpers.createAdminClient();
  const users = await adminClient.users.list({ total: false });

  return {
    session: helpers.readSessionCookie() ?? null,
    user,
    hasSessionClient: Boolean(sessionClient),
    adminUserCount: users.users.length,
  };
});

export const Route = createFileRoute("/")({
  loader: () => getAuthSnapshot(),
  component: Page,
});

function Page() {
  const { session, user, hasSessionClient, adminUserCount } = Route.useLoaderData();

  return (
    <AppwriteProvider
      endpoint={import.meta.env.VITE_APPWRITE_ENDPOINT}
      projectId={import.meta.env.VITE_APPWRITE_PROJECT_ID}
      ssr={{ session, basePath: "/api/appwrite" }}
    >
      <main>
        <p>SSR user: {user?.email ?? "signed out"}</p>
        <p>Session client: {hasSessionClient ? "available" : "none"}</p>
        <p>Admin users loaded: {adminUserCount}</p>
        <AuthPanel />
      </main>
    </AppwriteProvider>
  );
}

function AuthPanel() {
  const { user, isLoading, signIn, signOut } = useAuth();
  const router = useRouter();

  if (isLoading) return <p>Loading...</p>;

  if (!user) {
    return (
      <button
        onClick={() =>
          signIn.emailPassword({
            email: "user@example.com",
            password: "password123",
            onSuccess: () => router.invalidate(),
          })
        }
      >
        Sign in
      </button>
    );
  }

  return (
    <button
      onClick={() => signOut.signOut({ onSuccess: () => router.invalidate() })}
    >
      Sign out
    </button>
  );
}

API Overview

Client entrypoint:

import {
  AppwriteProvider,
  OAuthProvider,
  useAuth,
  useSignIn,
  useSignOut,
  useSignUp,
  useUser,
} from "@appwrite.io/react";

Server entrypoints:

import { createAppwriteHandlers as createNextAppwriteHandlers } from "@appwrite.io/react/handlers/next";
import { createAppwriteHandlers as createTanStackAppwriteHandlers } from "@appwrite.io/react/handlers/tanstack";

import {
  createAdminClient,
  createSessionClient,
} from "@appwrite.io/react/server";
import { createNextServerHelpers } from "@appwrite.io/react/server/next";
import { createTanStackServerHelpers } from "@appwrite.io/react/server/tanstack";

Do not import server entrypoints from client components or browser-only code.

Learn More

License

Please see the MIT license file for more information.

About

React SDK for Appwrite.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors