React
Hooks and wrapper components from @getauthui/core/react.
npm install @getauthui/core appwrite litimport {
AuthUIProvider,
AuthUIButton,
AuthUIUserButton,
Show,
useAuthUI,
} from "@getauthui/core/react";
const config = {
endpoint: "https://cloud.appwrite.io/v1",
project: "YOUR_PROJECT_ID",
methods: { emailPassword: true, oauth: ["google"] },
};
export function App() {
return (
<AuthUIProvider config={config}>
<Header />
</AuthUIProvider>
);
}
function Header() {
const { status, user, open, signOut } = useAuthUI();
return (
<header>
<Show when="signed-out">
<AuthUIButton>Sign in</AuthUIButton>
</Show>
<Show when="signed-in">
<span>{user?.name}</span>
<AuthUIUserButton />
</Show>
{status === "signed-in" && <button onClick={signOut}>Sign out</button>}
<button onClick={() => open("account")}>Account</button>
</header>
);
}useAuthUI()
Returns the current state plus helpers, and re-renders on every change.
| Field | Type | Description |
|---|---|---|
status | "loading" / "signed-out" / "signed-in" / "mfa-required" | Current state. |
user | Models.User | null | The signed-in user. |
mfaFactors | Models.MfaFactors | null | Available factors while status is mfa-required. |
pending | pending action or null | A redirect flow waiting to be finished. |
configured | boolean | Whether a complete config (endpoint and project) was applied. |
configError | string | null | Developer-facing config problem, or null. |
open(view?) | function | Open the modal. |
close() | function | Close the modal. |
signOut() | function | End the current session. |
store | AuthStore | Full imperative API. |
Components
| Component | Wraps | Props |
|---|---|---|
AuthUIProvider | configures the store synchronously | config |
AuthUIModal | <authui-modal> | view, open, closeOnSuccess, onOpenChange, onSignedIn, onClose |
AuthUIButton | <authui-button> | view, variant, size, children |
AuthUIUserButton | <authui-user-button> | src, showTeams, menuItems, onMenuAction, children |
AuthUISignIn | <authui-sign-in> | view, email, loginHint, onSuccess |
AuthUIAccount | <authui-account> | tab |
Show | conditional render (not Lit slot) | when, unless, children |
Show vs <authui-show>
The React Show wrapper returns null when the status does not match, so children are not mounted and their effects do not run. That is what you want when a child calls store.getClient() on mount.
The Lit <authui-show> element only skips its slot. Children stay in the DOM (hidden). Prefer the React Show in React apps; keep <authui-show> for plain HTML.
AuthUIProvider configuration timing
AuthUIProvider calls configure() during render, so the first paint already sees configured: true and a non-null getClient() when endpoint and project are set. If either is empty, it mirrors <authui-config>: configured stays false and configError explains what is missing. Changing any field on config (methods, branding, preview, strings, and so on) reconfigures the store. Passing a new object every render with the same values is fine; the provider fingerprints a canonical (sorted-key) JSON of the config, so key insertion order does not matter.
closeOnSuccess={false}
Pass closeOnSuccess={false} to AuthUIModal to keep the modal open after sign in. In HTML use close-on-success="false" on <authui-modal> (the string "false" is required; a bare boolean attribute would stay on).
Controlled open
Pass open as a boolean to drive the dialog from React state. open={true} opens it; open={false} force-closes it (including after the user opened it another way). Omit open to leave the modal uncontrolled.
When the user closes via Escape, backdrop click, or the X button, Lit hides the dialog. Wire onOpenChange so parent state stays in sync; otherwise the next setOpen(true) is a no-op because React still thinks the dialog is open.
const [modalOpen, setModalOpen] = useState(false);
<>
<button onClick={() => setModalOpen(true)}>Open</button>
<AuthUIModal open={modalOpen} onOpenChange={setModalOpen} />
</>;onOpenChange(false) runs when the dialog closes. onClose is a convenience alias for that case. onSignedIn bridges the Lit authui-signed-in event so Modal + Button apps do not need to subscribe to the store by hand.
<AuthUIModal
open={modalOpen}
onOpenChange={setModalOpen}
onSignedIn={(user) => console.log(user.$id)}
onClose={() => console.log("closed")}
/>Setting open to false writes through to the element so the dialog closes even when it was opened via AuthUI.open() or a button.
Typing custom elements in JSX
If you prefer to use the elements directly in TSX, add a declaration:
import type { AuthUIView } from "@getauthui/core";
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"authui-button": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
view?: AuthUIView;
variant?: string;
size?: "sm" | "md" | "lg";
},
HTMLElement
>;
"authui-modal": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
view?: AuthUIView;
open?: boolean;
"close-on-success"?: boolean | string;
},
HTMLElement
>;
"authui-sign-in": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
view?: AuthUIView;
email?: string;
"login-hint"?: string;
},
HTMLElement
>;
"authui-account": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & { tab?: string },
HTMLElement
>;
"authui-user-button": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
src?: string;
"show-teams"?: boolean | string;
},
HTMLElement
>;
"authui-show": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & { when?: string; unless?: string },
HTMLElement
>;
"authui-config": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & { endpoint: string; project: string },
HTMLElement
>;
}
}
}FOUC CSS in React
Import @getauthui/core/fouc.css (or the CDN dist/fouc.css link). Prefer authui-show:not([ready]) over :defined for FOUC CSS. React can define custom elements before Auth UI sets ready, so [ready] is the reliable gate. See <authui-show>.
Next.js
The components touch window, so import the package from a client component or inside useEffect. The wrappers in @getauthui/core/react are safe to import in client components.