Docs
Skip to content

Auth

React library_

Add authentication to React apps with Appwrite's official React library. Supports client-side React, Next.js, and TanStack Start with a single provider and a small set of hooks.

9 min read

Raw

The Appwrite React library is a thin layer over the Web SDK that exposes a provider and a small set of hooks for authentication operations and current user state. It works in both client-rendered React apps and server-rendered apps on Next.js and TanStack Start.

Why use it

  • SSR auth without boilerplate. Drop in one handler route per framework and skip the days normally spent writing cookie logic, session sync, and server/client hydration.
  • Consistent user state across server and client. Server components, loaders, and client hooks return the same authenticated user, removing the need to reconcile auth state across the render boundary.
  • Server-side access when you need it. Read the current user, create per-request session clients, or reach for an admin client without hand-wiring the Node SDK on every route.

Install

Install the library along with the Appwrite Web SDK and TanStack Query packages.

Bash
npm install @appwrite.io/react appwrite @tanstack/react-query

For SSR apps on Next.js or TanStack Start, also install node-appwrite. The SSR handlers create sessions with a server API key.

Bash
npm install @appwrite.io/react appwrite node-appwrite @tanstack/react-query

Pick your framework

The provider

Every app starts with an AppwriteProvider. In a CSR app, only endpoint and projectId are required.

TypeScript
import { AppwriteProvider } from "@appwrite.io/react";
<AppwriteProvider
endpoint={import.meta.env.VITE_APPWRITE_ENDPOINT}
projectId={import.meta.env.VITE_APPWRITE_PROJECT_ID}
>
<App />
</AppwriteProvider>;

In an SSR app, pass an ssr prop with the session secret read from the server-side cookie and the path your handlers are mounted at.

TypeScript
<AppwriteProvider
endpoint={endpoint}
projectId={projectId}
ssr={{ session, basePath: "/api/appwrite" }}
>
{children}
</AppwriteProvider>

When ssr is set, sign-in, sign-up, and sign-out mutations route through your handler. The underlying Web SDK is hydrated with the session secret on first render, so authenticated reads do not need a round trip.

Hooks

The library exports one combined hook and four focused mutation hooks. All hooks share the same TanStack Query cache, so updating user state from any of them reflects everywhere.

useAuth

The primary hook for most apps. It bundles the current user, loading and error state, and handles for sign-in, sign-up, and sign-out into a single return value.

TypeScript
import { useAuth } from "@appwrite.io/react";
const { user, isLoading, error, refresh, signIn, signUp, signOut } = useAuth();
FieldTypeDescription
userModels.User | null | undefinedThe current user, or null if signed out. undefined while loading.
isLoadingbooleanTrue while the initial user fetch is in flight.
errorError | nullFirst error from the user query or any mutation.
refresh() => Promise<User | null>Refetch the current user and return the resolved value.
signInReturnType<typeof useSignIn>Sign-in mutation handle.
signUpReturnType<typeof useSignUp>Sign-up mutation handle.
signOutReturnType<typeof useSignOut>Sign-out mutation handle.

useUser

Read-only access to the current authenticated user. Use it in components that only need to display user state and do not trigger auth mutations.

TypeScript
import { useUser } from "@appwrite.io/react";
const { user, isLoading, error, refresh } = useUser();
FieldTypeDescription
userModels.User | null | undefinedThe current user, or null if signed out. undefined while loading.
isLoadingbooleanTrue while the user is being fetched.
errorError | nullError from the fetch, if one occurred.
refresh() => Promise<User | null>Refetch the current user and return the resolved value.

useSignIn

Email and password and OAuth sign-in, with pending and error state scoped to the email and password flow.

TypeScript
const { emailPassword, oAuth, isPending, error } = useSignIn();
FieldTypeDescription
emailPasswordfunctionTrigger an email and password sign-in.
oAuthfunctionTrigger an OAuth sign-in.
isPendingbooleanTrue while an email and password sign-in is in flight.
errorError | nullError from the last email and password sign-in attempt.

useSignUp

Create an account and start a session in one call. The hook also surfaces pending and error state for the in-flight request.

TypeScript
const { emailPassword, isPending, error } = useSignUp();
FieldTypeDescription
emailPasswordfunctionCreate an account with email and password, then sign the user in.
isPendingbooleanTrue while the sign-up is in flight.
errorError | nullError from the last sign-up attempt.

Email and password

TypeScript
emailPassword({ email, password, name, userId, onSuccess, onError });
ParameterRequiredDescription
emailYesNew user's email address.
passwordYesNew user's password.
nameNoDisplay name for the user.
userIdNoCustom user ID. Defaults to ID.unique().
onSuccessNoCallback invoked with the created user.
onErrorNoCallback invoked with the thrown error.

useSignOut

End the current session, drop cached auth queries, and reset the local Web SDK client.

TypeScript
const { signOut, isPending, error } = useSignOut();
FieldTypeDescription
signOutfunctionEnd the current user's session.
isPendingbooleanTrue while the sign-out is in flight.
errorError | nullError from the last sign-out attempt.

Sign out

TypeScript
signOut({ onSuccess, onError });
ParameterRequiredDescription
onSuccessNoCallback invoked after the session is destroyed.
onErrorNoCallback invoked with the thrown error.

After a successful sign-out, all cached auth queries are dropped and the local Web SDK client is reset. On Next.js, call router.refresh() from onSuccess to re-render server components with the cleared cookie. On TanStack Start, call router.invalidate().

useAppwrite

Escape hatch to the underlying provider context. Use it when you need direct access to a Web SDK service the higher-level hooks do not wrap (tablesDB, storage, messaging, realtime, and so on).

TypeScript
import { useAppwrite } from "@appwrite.io/react";
const { client, account, tablesDB, storage, teams, ssr } = useAppwrite();
FieldTypeDescription
clientClientThe Appwrite Web SDK Client configured by the provider.
accountAccountWeb SDK Account service.
avatarsAvatarsWeb SDK Avatars service.
functionsFunctionsWeb SDK Functions service.
graphqlGraphqlWeb SDK Graphql service.
localeLocaleWeb SDK Locale service.
messagingMessagingWeb SDK Messaging service.
presencesPresencesWeb SDK Presences service.
realtimeRealtimeWeb SDK Realtime service.
storageStorageWeb SDK Storage service.
tablesDBTablesDBWeb SDK TablesDB service.
teamsTeamsWeb SDK Teams service.
authenticatedbooleanWhether the provider currently considers a user signed in.
setAuthenticatedDispatch<SetStateAction<boolean>>Update the authenticated flag manually.
ssr.enabledbooleanWhether the provider was mounted with the ssr prop.
ssr.basePathstringMount path of the handler routes.
ssr.sessionstring | nullSession secret read from the server cookie.

Throws if called outside an AppwriteProvider.

Server helpers

The library ships server entrypoints scoped per framework. Each helper exposes the same surface, differing only in how it reads the request cookie.

TypeScript
// Next.js (App Router)
import { createNextServerHelpers } from "@appwrite.io/react/server/next";
// TanStack Start
import { createTanStackServerHelpers } from "@appwrite.io/react/server/tanstack";

Both return an object with:

MethodReturnsUse it for
readSessionCookie()string | undefined (sync on TanStack, async on Next.js)The session secret. Pass it into AppwriteProvider's ssr.session prop.
getLoggedInUser()Models.User | nullThe current user, fetched via the cookie. Returns null on 401.
getSession()Models.Session | nullThe current session row.
createSessionClient()NodeSessionServer | nullA node-appwrite client authenticated as the cookie's user, or null if no cookie.
createAdminClient()AdminServerA node-appwrite admin client authenticated with the API key passed to the helper. Only available when apiKey is set in the helper config.

Session client

For per-request operations scoped to the current user (reading their data, calling APIs on their behalf), call createSessionClient from the framework helper. It returns a node-appwrite client already authenticated with the session cookie, ready to use in server components, loaders, and server functions.

TypeScript
import { createNextServerHelpers } from "@appwrite.io/react/server/next";
const helpers = createNextServerHelpers({
endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
});
const session = await helpers.createSessionClient();
if (session) {
const user = await session.account.get();
}

Admin client

For privileged operations (creating users, listing sessions, managing teams), call createAdminClient on the framework helper. Pass apiKey in the helper config to enable it, then call the method anywhere on the server. The returned object exposes every node-appwrite service: account, users, tablesDB, storage, teams, functions, messaging, and more.

TypeScript
import { createNextServerHelpers } from "@appwrite.io/react/server/next";
const helpers = createNextServerHelpers({
endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
apiKey: process.env.APPWRITE_API_KEY!,
});
const admin = helpers.createAdminClient();
const users = await admin.users.list();

If you would rather not pass the API key into the helper, the library also exports a standalone createAdminClient from @appwrite.io/react/server that takes the key directly.

TypeScript
import { createAdminClient } from "@appwrite.io/react/server";
const admin = createAdminClient({
endpoint: process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!,
projectId: process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!,
apiKey: process.env.APPWRITE_API_KEY!,
});
const users = await admin.users.list();

Never import any @appwrite.io/react/server/* module from client code: the entrypoints are marked server-only and will throw if bundled into the browser.

Handler routes

The SSR mutations (sign-in, sign-up, sign-out, oauth/callback, oauth/failure) live behind a handler route you mount once per app. The handler reads the request, talks to Appwrite with the server API key, and writes the session cookie back.

Cookie and redirect options

Customize how sessions persist in the browser and where users land after an OAuth round trip.

TypeScript
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",
cookieName: "my-app-session",
cookieOptions: {
sameSite: "strict",
domain: ".example.com",
},
redirects: {
success: "/dashboard",
failure: "/login?error=oauth",
},
});
OptionDefaultDescription
cookieNameappwrite-session-<projectId>Name of the HTTP-only session cookie.
cookieOptions.securetrueSet the Secure flag on the cookie. Modern browsers accept this on localhost over HTTP.
cookieOptions.sameSite"lax"SameSite policy.
cookieOptions.httpOnlytrueHttpOnly flag.
cookieOptions.path"/"Cookie path.
cookieOptions.domainunsetRestrict cookie to a domain.
redirects.success"/"URL to redirect to after a successful OAuth login.
redirects.failure"/"URL to redirect to after a failed OAuth attempt.

Required scopes

Your server API key needs three scopes for the SSR handler to function:

  • users.write (sign-up creates a user)
  • users.read (sign-up confirms the created user)
  • sessions.write (sign-in, sign-out, and OAuth callbacks create or delete sessions)

Next steps

Was this page helpful?

Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.