diff --git a/examples/react/.gitignore b/examples/react/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/examples/react/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/react/README.md b/examples/react/README.md
new file mode 100644
index 0000000..7059a96
--- /dev/null
+++ b/examples/react/README.md
@@ -0,0 +1,12 @@
+# React + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
diff --git a/examples/react/eslint.config.js b/examples/react/eslint.config.js
new file mode 100644
index 0000000..ec2b712
--- /dev/null
+++ b/examples/react/eslint.config.js
@@ -0,0 +1,33 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+
+export default [
+ { ignores: ['dist'] },
+ {
+ files: ['**/*.{js,jsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ ecmaFeatures: { jsx: true },
+ sourceType: 'module',
+ },
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...js.configs.recommended.rules,
+ ...reactHooks.configs.recommended.rules,
+ 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ },
+]
diff --git a/examples/react/index.html b/examples/react/index.html
new file mode 100644
index 0000000..183149b
--- /dev/null
+++ b/examples/react/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Vite + React
+
+
+
+
+
+
+
diff --git a/examples/react/lib/components/header.tsx b/examples/react/lib/components/header.tsx
new file mode 100644
index 0000000..b50f2e2
--- /dev/null
+++ b/examples/react/lib/components/header.tsx
@@ -0,0 +1,30 @@
+'use client';
+
+import { NavLink } from "react-router";
+import { useUser } from "../firebase/hooks";
+import { signOut, type User } from "firebase/auth";
+import { auth } from "../firebase/clientApp";
+
+export function Header() {
+ const user = useUser();
+
+ async function onSignOut() {
+ await signOut(auth);
+ router.push("/sign-in");
+ }
+
+ return (
+
+
+
+ FirebaseUI
+
+
+
+ {user ? : - Sign In
}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/examples/react/lib/firebase/clientApp.ts b/examples/react/lib/firebase/clientApp.ts
new file mode 100644
index 0000000..72c3c55
--- /dev/null
+++ b/examples/react/lib/firebase/clientApp.ts
@@ -0,0 +1,34 @@
+"use client";
+
+import { initializeApp, getApps } from "firebase/app";
+import { firebaseConfig } from "./config";
+import { connectAuthEmulator, getAuth } from "firebase/auth";
+import { autoAnonymousLogin, initializeUI } from "@firebase-ui/core";
+import { customLanguage, english } from "@firebase-ui/translations";
+
+export const firebaseApp =
+ getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
+
+export const auth = getAuth(firebaseApp);
+
+export const ui = initializeUI({
+ app: firebaseApp,
+ behaviors: [autoAnonymousLogin()],
+ translations: [
+ customLanguage(english.locale, {
+ labels: {
+ signIn: "Sign In",
+ },
+ prompts: {
+ signInToAccount: "Sign in to your account",
+ },
+ errors: {
+ invalidEmail: "Please enter a valid email address",
+ },
+ }),
+ ],
+});
+
+if (import.meta.env.MODE === "development") {
+ connectAuthEmulator(auth, "http://localhost:9099");
+}
diff --git a/examples/react/lib/firebase/config.ts b/examples/react/lib/firebase/config.ts
new file mode 100644
index 0000000..fee2fca
--- /dev/null
+++ b/examples/react/lib/firebase/config.ts
@@ -0,0 +1,11 @@
+export const firebaseConfig = {
+ apiKey: "AIzaSyAotbJXqnZxg9aAsULFn8MLwp_twtMUl2k",
+ authDomain: "ff-test-74aeb.firebaseapp.com",
+ databaseURL:
+ "https://ff-test-74aeb-default-rtdb.asia-southeast1.firebasedatabase.app",
+ projectId: "ff-test-74aeb",
+ storageBucket: "ff-test-74aeb.appspot.com",
+ messagingSenderId: "950537677105",
+ appId: "1:950537677105:web:da72ccc1718279f3cde810",
+ measurementId: "G-B5Y2YD83TJ",
+};
diff --git a/examples/react/lib/firebase/hooks.ts b/examples/react/lib/firebase/hooks.ts
new file mode 100644
index 0000000..56bc7ce
--- /dev/null
+++ b/examples/react/lib/firebase/hooks.ts
@@ -0,0 +1,17 @@
+import { useState } from "react";
+
+import { onAuthStateChanged } from "firebase/auth";
+import { User } from "firebase/auth";
+import { useEffect } from "react";
+import { auth } from "./clientApp";
+
+export function useUser(initalUser?: User | null) {
+ const [user, setUser] = useState(initalUser ?? null);
+
+ useEffect(() => {
+ const unsubscribe = onAuthStateChanged(auth, setUser);
+ return () => unsubscribe();
+ }, []);
+
+ return user;
+}
\ No newline at end of file
diff --git a/examples/react/lib/firebase/ui.tsx b/examples/react/lib/firebase/ui.tsx
new file mode 100644
index 0000000..e8a97cb
--- /dev/null
+++ b/examples/react/lib/firebase/ui.tsx
@@ -0,0 +1,22 @@
+"use client";
+
+import { ui } from "./clientApp";
+import { ConfigProvider } from "@firebase-ui/react";
+
+export function FirebaseUIProvider({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/examples/react/package.json b/examples/react/package.json
new file mode 100644
index 0000000..4f1ee4f
--- /dev/null
+++ b/examples/react/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "react",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@firebase-ui/core": "workspace:*",
+ "@firebase-ui/react": "workspace:*",
+ "@firebase-ui/styles": "workspace:*",
+ "@firebase-ui/translations": "workspace:*",
+ "firebase": "^11.6.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-router": "^7.5.1"
+ },
+ "devDependencies": {
+ "@tailwindcss/vite": "^4.1.4",
+ "@eslint/js": "^9.22.0",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "eslint": "^9.22.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^16.0.0",
+ "vite": "^6.3.1",
+ "tailwindcss": "^4.1.4"
+ }
+}
diff --git a/examples/react/public/vite.svg b/examples/react/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/examples/react/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/react/src/App.css b/examples/react/src/App.css
new file mode 100644
index 0000000..e69de29
diff --git a/examples/react/src/App.jsx b/examples/react/src/App.jsx
new file mode 100644
index 0000000..d1ec242
--- /dev/null
+++ b/examples/react/src/App.jsx
@@ -0,0 +1,110 @@
+import { NavLink } from "react-router";
+import { useUser } from "../lib/firebase/hooks";
+
+function App() {
+ const user = useUser();
+
+ return (
+
+
Firebase UI Demo
+
+ {user &&
Welcome: {user.email || user.phoneNumber}
}
+
+
+
Auth Screens
+
+ -
+
+ Sign In Auth Screen
+
+
+ -
+
+ Sign In Auth Screen with Handlers
+
+
+ -
+
+ Sign In Auth Screen with OAuth
+
+
+ -
+
+ Email Link Auth Screen
+
+
+ -
+
+ Email Link Auth Screen with OAuth
+
+
+ -
+
+ Phone Auth Screen
+
+
+ -
+
+ Phone Auth Screen with OAuth
+
+
+ -
+
+ Sign Up Auth Screen
+
+
+ -
+
+ Sign Up Auth Screen with OAuth
+
+
+ -
+
+ OAuth Screen
+
+
+ -
+
+ Password Reset Screen
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/examples/react/src/assets/react.svg b/examples/react/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/examples/react/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/react/src/index.css b/examples/react/src/index.css
new file mode 100644
index 0000000..2931ddf
--- /dev/null
+++ b/examples/react/src/index.css
@@ -0,0 +1,5 @@
+@import "tailwindcss";
+@import "@firebase-ui/styles/src/base.css";
+
+/* @import "@firebase-ui/styles/src/themes/dark.css"; */
+/* @import "@firebase-ui/styles/src/themes/brutalist.css"; */
\ No newline at end of file
diff --git a/examples/react/src/main.jsx b/examples/react/src/main.jsx
new file mode 100644
index 0000000..abbda05
--- /dev/null
+++ b/examples/react/src/main.jsx
@@ -0,0 +1,85 @@
+import { BrowserRouter, RouterProvider, Routes, Route } from "react-router";
+
+import React from "react";
+import ReactDOM from "react-dom/client";
+
+import App from "./App";
+import { Header } from "../lib/components/header";
+import { FirebaseUIProvider } from "../lib/firebase/ui";
+
+/** Sign In */
+import SignInAuthScreenPage from "./screens/sign-in-auth-screen";
+import SignInAuthScreenWithHandlersPage from "./screens/sign-in-auth-screen-w-handlers";
+import SignInAuthScreenWithOAuthPage from "./screens/sign-in-auth-screen-w-oauth";
+
+/** Email */
+import EmailLinkAuthScreenPage from "./screens/email-link-auth-screen";
+import EmailLinkAuthScreenWithOAuthPage from "./screens/email-link-auth-screen-w-oauth";
+
+/** Phone Auth */
+import PhoneAuthScreenPage from "./screens/phone-auth-screen";
+import PhoneAuthScreenWithOAuthPage from "./screens/phone-auth-screen-w-oauth";
+
+/** Sign up */
+import SignUpAuthScreenPage from "./screens/sign-in-auth-screen-w-oauth";
+import SignUpAuthScreenWithOAuthPage from "./screens/sign-in-auth-screen";
+
+/** oAuth */
+import OAuthScreenPage from "./screens/oauth-screen";
+
+/** Password Reset */
+import PasswordResetScreenPage from "./screens/password-reset-screen";
+
+const root = document.getElementById("root");
+
+ReactDOM.createRoot(root).render(
+
+
+
+
+ } />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+ } />
+ }
+ />
+
+
+
+);
diff --git a/examples/react/src/screens/email-link-auth-screen-w-oauth.tsx b/examples/react/src/screens/email-link-auth-screen-w-oauth.tsx
new file mode 100644
index 0000000..074231f
--- /dev/null
+++ b/examples/react/src/screens/email-link-auth-screen-w-oauth.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { EmailLinkAuthScreen, GoogleSignInButton } from "@firebase-ui/react";
+
+export default function EmailLinkAuthScreenWithOAuthPage() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/src/screens/email-link-auth-screen.tsx b/examples/react/src/screens/email-link-auth-screen.tsx
new file mode 100644
index 0000000..f7c3045
--- /dev/null
+++ b/examples/react/src/screens/email-link-auth-screen.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EmailLinkAuthScreen } from "@firebase-ui/react";
+
+export default function EmailLinkAuthScreenPage() {
+ return ;
+}
diff --git a/examples/react/src/screens/oauth-screen.tsx b/examples/react/src/screens/oauth-screen.tsx
new file mode 100644
index 0000000..f0fb139
--- /dev/null
+++ b/examples/react/src/screens/oauth-screen.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { GoogleSignInButton, OAuthScreen } from "@firebase-ui/react";
+
+export default function OAuthScreenPage() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/src/screens/password-reset-screen.tsx b/examples/react/src/screens/password-reset-screen.tsx
new file mode 100644
index 0000000..9b029a2
--- /dev/null
+++ b/examples/react/src/screens/password-reset-screen.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { PasswordResetScreen } from "@firebase-ui/react";
+
+export default function PasswordResetScreenPage() {
+ return (
+ {}}
+ />
+ );
+}
diff --git a/examples/react/src/screens/phone-auth-screen-w-oauth.tsx b/examples/react/src/screens/phone-auth-screen-w-oauth.tsx
new file mode 100644
index 0000000..97e883a
--- /dev/null
+++ b/examples/react/src/screens/phone-auth-screen-w-oauth.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { GoogleSignInButton, PhoneAuthScreen } from "@firebase-ui/react";
+
+export default function PhoneAuthScreenWithOAuthPage() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/src/screens/phone-auth-screen.tsx b/examples/react/src/screens/phone-auth-screen.tsx
new file mode 100644
index 0000000..d63d16c
--- /dev/null
+++ b/examples/react/src/screens/phone-auth-screen.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { PhoneAuthScreen } from "@firebase-ui/react";
+
+export default function PhoneAuthScreenPage() {
+ return ;
+}
diff --git a/examples/react/src/screens/sign-in-auth-screen-w-handlers.tsx b/examples/react/src/screens/sign-in-auth-screen-w-handlers.tsx
new file mode 100644
index 0000000..fdc8c86
--- /dev/null
+++ b/examples/react/src/screens/sign-in-auth-screen-w-handlers.tsx
@@ -0,0 +1,12 @@
+"use client";
+
+import { SignInAuthScreen } from "@firebase-ui/react";
+
+export default function SignInAuthScreenWithHandlersPage() {
+ return (
+ {}}
+ onRegisterClick={() => {}}
+ />
+ );
+}
diff --git a/examples/react/src/screens/sign-in-auth-screen-w-oauth.tsx b/examples/react/src/screens/sign-in-auth-screen-w-oauth.tsx
new file mode 100644
index 0000000..944850f
--- /dev/null
+++ b/examples/react/src/screens/sign-in-auth-screen-w-oauth.tsx
@@ -0,0 +1,17 @@
+"use client";
+
+import { GoogleSignInButton, SignInAuthScreen } from "@firebase-ui/react";
+import { useNavigate } from "react-router";
+
+export default function SignInAuthScreenWithOAuthPage() {
+ let navigate = useNavigate();
+
+ return (
+ navigate("/password-reset-screen")}
+ onRegisterClick={() => navigate("/sign-up-auth-screen")}
+ >
+
+
+ );
+}
diff --git a/examples/react/src/screens/sign-in-auth-screen.tsx b/examples/react/src/screens/sign-in-auth-screen.tsx
new file mode 100644
index 0000000..3311477
--- /dev/null
+++ b/examples/react/src/screens/sign-in-auth-screen.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { SignInAuthScreen } from "@firebase-ui/react";
+
+export default function SignInAuthScreenPage() {
+ return ;
+}
diff --git a/examples/react/src/screens/sign-up-auth-screen-w-oauth.tsx b/examples/react/src/screens/sign-up-auth-screen-w-oauth.tsx
new file mode 100644
index 0000000..bf57c48
--- /dev/null
+++ b/examples/react/src/screens/sign-up-auth-screen-w-oauth.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import { GoogleSignInButton, SignUpAuthScreen } from "@firebase-ui/react";
+
+export default function SignUpAuthScreenWithOAuthPage() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/src/screens/sign-up-auth-screen.tsx b/examples/react/src/screens/sign-up-auth-screen.tsx
new file mode 100644
index 0000000..8fb529c
--- /dev/null
+++ b/examples/react/src/screens/sign-up-auth-screen.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { SignUpAuthScreen } from "@firebase-ui/react";
+
+export default function SignUpAuthScreenPage() {
+ return ;
+}
diff --git a/examples/react/vite.config.js b/examples/react/vite.config.js
new file mode 100644
index 0000000..8a24cd2
--- /dev/null
+++ b/examples/react/vite.config.js
@@ -0,0 +1,7 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+
+export default defineConfig({
+ plugins: [tailwindcss(), react()],
+});