diff --git a/community/privy-auth/.env.example b/community/privy-auth/.env.example
new file mode 100644
index 00000000..c47731ac
--- /dev/null
+++ b/community/privy-auth/.env.example
@@ -0,0 +1,8 @@
+# Privy App ID — Get yours at https://dashboard.privy.io
+NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id
+
+# Privy App Secret — Get yours at https://dashboard.privy.io (Settings > API Keys)
+PRIVY_APP_SECRET=your-privy-app-secret
+
+# Solana RPC URL (defaults to devnet if not set)
+NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
diff --git a/community/privy-auth/.gitignore b/community/privy-auth/.gitignore
new file mode 100644
index 00000000..a0530a8f
--- /dev/null
+++ b/community/privy-auth/.gitignore
@@ -0,0 +1,33 @@
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files
+.env*.local
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/community/privy-auth/.prettierrc b/community/privy-auth/.prettierrc
new file mode 100644
index 00000000..7e7cba01
--- /dev/null
+++ b/community/privy-auth/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "semi": true,
+ "singleQuote": false,
+ "tabWidth": 2,
+ "trailingComma": "all"
+}
diff --git a/community/privy-auth/README.md b/community/privy-auth/README.md
new file mode 100644
index 00000000..41891424
--- /dev/null
+++ b/community/privy-auth/README.md
@@ -0,0 +1,433 @@
+
+
+
+
+
+
+privy-auth
+
+
+ Solana dApp template with Privy authentication
+
+
+
+ Social logins • Embedded wallets • Protected routes • Ready to ship
+
+
+
+ Quick Start •
+ Features •
+ Setup •
+ Customize •
+ Resources
+
+
+
+
+
+
+
+
+
+
+
+---
+
+## Quick Start
+
+```bash
+# Scaffold with create-solana-dapp (recommended)
+pnpm create solana-dapp --template privy-auth
+
+# Or clone directly
+npx degit solana-developers/solana-templates/community/privy-auth my-app
+cd my-app && pnpm install
+```
+
+
+npm / yarn
+
+```bash
+# npm
+npx create-solana-dapp --template privy-auth
+
+# yarn
+yarn create solana-dapp --template privy-auth
+```
+
+
+
+---
+
+## Features
+
+```
++-------------------+ +-------------------+ +-------------------+
+| Social Login | | Embedded Wallets | | Protected Routes |
+| | | | | |
+| Google | | Auto-created | | Middleware guard |
+| Twitter/X | | Solana wallet | | Client-side |
+| Discord | | for every user | | auth check |
+| Email | | on login | | |
++-------------------+ +-------------------+ +-------------------+
+
++-------------------+ +-------------------+ +-------------------+
+| External Wallets | | Sign Messages | | Wallet State |
+| | | | | |
+| Phantom | | Demo component | | Privy hooks for |
+| Solflare | | to sign with | | easy wallet |
+| Backpack | | embedded wallet | | access |
+| + more | | | | |
++-------------------+ +-------------------+ +-------------------+
+```
+
+---
+
+## Tech Stack
+
+| | Technology | Version |
+|---|-----------|---------|
+| **Framework** | Next.js (App Router) | ^15.3 |
+| **Language** | TypeScript | ^5 |
+| **Styling** | Tailwind CSS | ^4 |
+| **Auth** | Privy React SDK | ^3.0 |
+| **Blockchain** | Solana Kit | ^6.1 |
+
+---
+
+## Setup Guide
+
+### 1. Create a Privy App
+
+> **Takes ~2 minutes.** Free tier is all you need.
+
+1. Go to **[dashboard.privy.io](https://dashboard.privy.io)** and sign up
+2. Click **Create App**
+3. Enable login methods in the sidebar:
+
+ | Method | Toggle |
+ |--------|--------|
+ | Google | On |
+ | Twitter | On |
+ | Discord | On |
+ | Email | On |
+ | Wallet | On |
+
+4. Go to **Embedded Wallets** > ensure **Solana** is enabled
+5. Go to **Settings > API Keys** > copy your **App ID**
+
+### 2. Environment Variables
+
+```bash
+cp .env.example .env.local
+```
+
+```env
+# Required
+NEXT_PUBLIC_PRIVY_APP_ID=cmxxxxxxxxxxxxxxxxx
+
+# Optional — for server-side token verification
+PRIVY_APP_SECRET=privy_app_secret_xxxxxxxxx
+
+# Optional — defaults to devnet
+NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
+```
+
+### 3. Run
+
+```bash
+pnpm install
+pnpm dev
+```
+
+Open **[localhost:3000](http://localhost:3000)** — you should see the landing page.
+
+> If you see "Missing Privy App ID", double-check your `.env.local` file.
+
+---
+
+## Project Structure
+
+```
+privy-auth/
+│
+├── src/
+│ ├── app/
+│ │ ├── layout.tsx .............. Root layout with PrivyProvider
+│ │ ├── page.tsx ............... Landing page + login button
+│ │ ├── globals.css ............ Dark theme + Tailwind
+│ │ └── dashboard/
+│ │ └── page.tsx ........... Protected dashboard
+│ │
+│ ├── components/
+│ │ ├── providers.tsx .......... Privy config (logins, wallets, theme)
+│ │ ├── login-button.tsx ....... Opens modal, redirects on success
+│ │ ├── user-profile.tsx ....... User ID, wallets, linked accounts
+│ │ ├── wallet-info.tsx ........ Status indicators (green/red dots)
+│ │ └── sign-message.tsx ....... Sign demo with embedded wallet
+│ │
+│
+├── middleware.ts ................... Route guard (privy-token cookie)
+├── .env.example ................... Environment variable template
+└── package.json ................... Deps + create-solana-dapp config
+```
+
+---
+
+## How It Works
+
+### Auth Flow
+
+```
+ ┌─────────────────────┐
+ │ Landing Page │
+ │ (page.tsx) │
+ └──────────┬──────────┘
+ │
+ Click "Sign In with Privy"
+ │
+ ┌──────────▼──────────┐
+ │ Privy Modal │
+ │ │
+ │ ┌───────────────┐ │
+ │ │ Google │ │
+ │ │ Twitter/X │ │
+ │ │ Discord │ │
+ │ │ Email │ │
+ │ │ Wallet │ │
+ │ └───────────────┘ │
+ └──────────┬──────────┘
+ │
+ User authenticates
+ │
+ ┌──────────▼──────────┐
+ │ Embedded Solana │
+ │ wallet created │
+ │ automatically │
+ └──────────┬──────────┘
+ │
+ privy-token cookie set
+ │
+ ┌──────────▼──────────┐
+ │ /dashboard │
+ │ │
+ │ Profile + Wallets │
+ │ Sign Message Demo │
+ │ Logout │
+ └─────────────────────┘
+```
+
+### Route Protection
+
+Two layers keep `/dashboard` secure:
+
+| Layer | File | What it does |
+|-------|------|-------------|
+| **Middleware** | `middleware.ts` | No `privy-token` cookie? Redirect to `/` |
+| **Client** | `dashboard/page.tsx` | `usePrivy()` not authenticated? Redirect to `/` |
+
+---
+
+## Customization
+
+
+Change Login Methods
+
+Edit `src/components/providers.tsx`:
+
+```tsx
+loginMethods: ["google", "twitter", "discord", "email", "wallet"],
+```
+
+Available: `"google"`, `"twitter"`, `"discord"`, `"email"`, `"wallet"`, `"apple"`, `"github"`, `"linkedin"`, `"tiktok"`, `"farcaster"`, `"phone"`
+
+> Also toggle matching methods in **[Privy Dashboard](https://dashboard.privy.io) > Login Methods**.
+
+
+
+
+Change Theme & Appearance
+
+```tsx
+// src/components/providers.tsx
+appearance: {
+ theme: "dark", // "dark" | "light"
+ accentColor: "#9945FF", // any hex color
+ showWalletLoginFirst: false, // wallet options first?
+ walletChainType: "solana-only",
+},
+```
+
+
+
+
+Change Embedded Wallet Behavior
+
+```tsx
+// src/components/providers.tsx
+embeddedWallets: {
+ solana: {
+ createOnLogin: "users-without-wallets",
+ // "all-users" — wallet for everyone
+ // "users-without-wallets" — only if no external wallet
+ // "off" — no auto-creation
+ },
+},
+```
+
+
+
+
+Use the Wallet Hook
+
+```tsx
+import { useWallets } from "@privy-io/react-auth/solana";
+
+function MyComponent() {
+ const { wallets, ready } = useWallets();
+
+ // wallets — all connected Solana wallets
+ // ready — boolean (whether wallets are ready)
+
+ const embedded = wallets.find((w) => w.standardWallet.name === "Privy") ?? null;
+ const external = wallets.filter((w) => w.standardWallet.name !== "Privy");
+}
+```
+
+
+
+
+Sign Messages
+
+```tsx
+import {
+ useWallets,
+ useSignMessage,
+} from "@privy-io/react-auth/solana";
+
+function MyComponent() {
+ const { wallets } = useWallets();
+ const { signMessage } = useSignMessage();
+ const embedded = wallets.find((w) => w.standardWallet.name === "Privy");
+
+ const handleSign = async () => {
+ if (!embedded) return;
+ const msg = new TextEncoder().encode("Hello, Solana!");
+ const { signature } = await signMessage({
+ message: msg,
+ wallet: embedded,
+ });
+ console.log("Signature:", Buffer.from(signature).toString("base64"));
+ };
+}
+```
+
+
+
+
+Add a New Protected Route
+
+1. Create `src/app/dashboard/settings/page.tsx`
+2. Middleware already covers `/dashboard/*` — no config needed
+3. Add the client guard:
+
+```tsx
+"use client";
+import { usePrivy } from "@privy-io/react-auth";
+import { useRouter } from "next/navigation";
+import { useEffect } from "react";
+
+export default function SettingsPage() {
+ const { ready, authenticated } = usePrivy();
+ const router = useRouter();
+
+ useEffect(() => {
+ if (ready && !authenticated) router.replace("/");
+ }, [ready, authenticated, router]);
+
+ if (!ready) return Loading...
;
+ if (!authenticated) return null;
+
+ return Your protected content
;
+}
+```
+
+
+
+
+Switch to Mainnet
+
+```env
+NEXT_PUBLIC_SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
+```
+
+For production, use [Helius](https://helius.dev), [QuickNode](https://quicknode.com), or [Triton](https://triton.one).
+
+
+
+---
+
+## Scripts
+
+| Command | Description |
+|---------|-------------|
+| `pnpm dev` | Dev server at `localhost:3000` |
+| `pnpm build` | Production build |
+| `pnpm start` | Start production server |
+| `pnpm lint` | Run ESLint |
+
+---
+
+## Troubleshooting
+
+
+"Missing Privy App ID" screen
+
+Set `NEXT_PUBLIC_PRIVY_APP_ID` in `.env.local`. Get it from [dashboard.privy.io](https://dashboard.privy.io) > Settings > API Keys.
+
+
+
+
+Social login not working
+
+Enable the method in **both** places:
+1. `providers.tsx` > `loginMethods` array
+2. [Privy Dashboard](https://dashboard.privy.io) > Login Methods
+
+
+
+
+Embedded wallet not created
+
+Check `embeddedWallets.solana.createOnLogin` is `"users-without-wallets"` in `providers.tsx`. Also enable **Embedded Wallets > Solana** in the Privy Dashboard.
+
+
+
+
+Hydration errors in console
+
+Usually browser extensions (wallets, ad blockers) injecting DOM elements. Cosmetic only — doesn't affect functionality.
+
+
+
+---
+
+## Resources
+
+| | Link |
+|---|------|
+| Privy Docs | [docs.privy.io](https://docs.privy.io) |
+| Privy + Solana Guide | [Getting Started](https://docs.privy.io/recipes/solana/getting-started-with-privy-and-solana) |
+| Privy Dashboard | [dashboard.privy.io](https://dashboard.privy.io) |
+| Privy SDK Reference | [React Auth](https://docs.privy.io/reference/sdk/react-auth) |
+| Solana Docs | [solana.com/docs](https://solana.com/docs) |
+| create-solana-dapp | [GitHub](https://github.com/solana-developers/create-solana-dapp) |
+| Solana Templates | [GitHub](https://github.com/solana-developers/solana-templates) |
+
+---
+
+
+ Built with Privy + Solana
+
+ MIT License
+
diff --git a/community/privy-auth/components.json b/community/privy-auth/components.json
new file mode 100644
index 00000000..fdcefc1f
--- /dev/null
+++ b/community/privy-auth/components.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "src/app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "rtl": false,
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "registries": {}
+}
diff --git a/community/privy-auth/eslint.config.mjs b/community/privy-auth/eslint.config.mjs
new file mode 100644
index 00000000..c6d02a96
--- /dev/null
+++ b/community/privy-auth/eslint.config.mjs
@@ -0,0 +1,12 @@
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+import { FlatCompat } from "@eslint/eslintrc";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const compat = new FlatCompat({ baseDirectory: __dirname });
+
+const eslintConfig = [...compat.extends("next/core-web-vitals")];
+
+export default eslintConfig;
diff --git a/community/privy-auth/middleware.ts b/community/privy-auth/middleware.ts
new file mode 100644
index 00000000..02434369
--- /dev/null
+++ b/community/privy-auth/middleware.ts
@@ -0,0 +1,15 @@
+import { NextRequest, NextResponse } from "next/server";
+
+export function middleware(req: NextRequest) {
+ const privyToken = req.cookies.get("privy-token");
+
+ if (!privyToken && req.nextUrl.pathname.startsWith("/dashboard")) {
+ return NextResponse.redirect(new URL("/", req.url));
+ }
+
+ return NextResponse.next();
+}
+
+export const config = {
+ matcher: ["/dashboard/:path*"],
+};
diff --git a/community/privy-auth/next.config.ts b/community/privy-auth/next.config.ts
new file mode 100644
index 00000000..cb651cdc
--- /dev/null
+++ b/community/privy-auth/next.config.ts
@@ -0,0 +1,5 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {};
+
+export default nextConfig;
diff --git a/community/privy-auth/package.json b/community/privy-auth/package.json
new file mode 100644
index 00000000..c41d7720
--- /dev/null
+++ b/community/privy-auth/package.json
@@ -0,0 +1,80 @@
+{
+ "name": "privy-auth",
+ "version": "0.0.0",
+ "private": true,
+ "displayName": "Privy Auth",
+ "description": "Next.js Solana starter with Privy authentication, social logins, and embedded wallets",
+ "usecase": "Auth",
+ "keywords": [
+ "solana",
+ "nextjs",
+ "privy",
+ "auth",
+ "social-login",
+ "embedded-wallet",
+ "typescript",
+ "tailwind"
+ ],
+ "create-solana-dapp": {
+ "instructions": [
+ "Set up your Privy App:",
+ "1. Go to https://dashboard.privy.io and create a new app",
+ "2. Enable desired login methods (Google, Twitter, Discord, Email)",
+ "3. Copy your App ID and App Secret",
+ "",
+ "Configure environment variables:",
+ "Copy .env.example to .env.local and fill in your values",
+ "",
+ "Install dependencies:",
+ "+{pm} install",
+ "",
+ "Start dev server:",
+ "+{pm} dev"
+ ],
+ "rename": {
+ "privy-auth": {
+ "to": "{{name}}",
+ "paths": [
+ "README.md",
+ "src"
+ ]
+ }
+ }
+ },
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint ."
+ },
+ "dependencies": {
+ "@privy-io/react-auth": "^3.0.0",
+ "@radix-ui/react-separator": "^1.1.8",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@radix-ui/react-tooltip": "^1.2.8",
+ "@solana/kit": "^6.1.0",
+ "@solana-program/memo": "^0.8.0",
+ "@solana-program/system": "^0.12.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.575.0",
+ "next": "^15.3.0",
+ "next-themes": "^0.4.6",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.5.0",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3.3.4",
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "^15.3.0",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/community/privy-auth/postcss.config.mjs b/community/privy-auth/postcss.config.mjs
new file mode 100644
index 00000000..61e36849
--- /dev/null
+++ b/community/privy-auth/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/community/privy-auth/src/app/dashboard/page.tsx b/community/privy-auth/src/app/dashboard/page.tsx
new file mode 100644
index 00000000..ead7bf7e
--- /dev/null
+++ b/community/privy-auth/src/app/dashboard/page.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import { usePrivy } from "@privy-io/react-auth";
+import { useRouter } from "next/navigation";
+import { useEffect } from "react";
+import { Wallet } from "lucide-react";
+import { UserProfile } from "@/components/user-profile";
+import { WalletInfo } from "@/components/wallet-info";
+import { SignMessage } from "@/components/sign-message";
+import { ThemeToggle } from "@/components/theme-toggle";
+
+export default function DashboardPage() {
+ const { ready, authenticated } = usePrivy();
+ const router = useRouter();
+
+ useEffect(() => {
+ if (ready && !authenticated) {
+ router.replace("/");
+ }
+ }, [ready, authenticated, router]);
+
+ if (!ready) {
+ return (
+
+
+
+ );
+ }
+
+ if (!authenticated) return null;
+
+ return (
+
+ {/* Dot grid background */}
+
+
+ {/* Nav */}
+
+
+ {/* Content */}
+
+ {/* Header */}
+
+
Dashboard
+
+ Manage your wallet and account
+
+
+
+ {/* Cards */}
+
+
+
+ );
+}
diff --git a/community/privy-auth/src/app/globals.css b/community/privy-auth/src/app/globals.css
new file mode 100644
index 00000000..77f1803a
--- /dev/null
+++ b/community/privy-auth/src/app/globals.css
@@ -0,0 +1,248 @@
+@import "tailwindcss";
+
+@plugin "tailwindcss-animate";
+
+@custom-variant dark (&:is(.dark *));
+
+:root {
+ --radius: 0.75rem;
+ --background: oklch(0.985 0.002 90);
+ --foreground: oklch(0.145 0.015 260);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0.015 260);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.145 0.015 260);
+ --primary: oklch(0.48 0.22 277);
+ --primary-foreground: oklch(1 0 0);
+ --secondary: oklch(0.96 0.005 260);
+ --secondary-foreground: oklch(0.24 0.015 260);
+ --muted: oklch(0.955 0.005 260);
+ --muted-foreground: oklch(0.50 0.01 260);
+ --accent: oklch(0.955 0.005 260);
+ --accent-foreground: oklch(0.24 0.015 260);
+ --destructive: oklch(0.60 0.21 25);
+ --destructive-foreground: oklch(1 0 0);
+ --border: oklch(0.91 0.005 260);
+ --input: oklch(0.91 0.005 260);
+ --ring: oklch(0.48 0.22 277);
+ --chart-1: oklch(0.48 0.22 277);
+ --chart-2: oklch(0.68 0.13 182);
+ --chart-3: oklch(0.77 0.16 70);
+ --chart-4: oklch(0.65 0.21 354);
+ --chart-5: oklch(0.72 0.19 150);
+ --sidebar: oklch(0.985 0.002 90);
+ --sidebar-foreground: oklch(0.145 0.015 260);
+ --sidebar-primary: oklch(0.48 0.22 277);
+ --sidebar-primary-foreground: oklch(1 0 0);
+ --sidebar-accent: oklch(0.955 0.005 260);
+ --sidebar-accent-foreground: oklch(0.24 0.015 260);
+ --sidebar-border: oklch(0.91 0.005 260);
+ --sidebar-ring: oklch(0.48 0.22 277);
+ --font-sans: DM Sans, sans-serif;
+ --font-serif: DM Sans, sans-serif;
+ --font-mono: Space Mono, monospace;
+ --shadow-color: #1a1a1a;
+ --shadow-opacity: 0.05;
+ --shadow-blur: 0px;
+ --shadow-spread: 0px;
+ --shadow-offset-x: 0px;
+ --shadow-offset-y: 0px;
+ --letter-spacing: normal;
+ --spacing: 0.25rem;
+ --shadow-2xs: 0 1px 2px 0 rgb(0 0 0 / 0.02);
+ --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.03);
+ --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.04), 0 1px 2px -1px rgb(0 0 0 / 0.04);
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.06), 0 2px 4px -2px rgb(0 0 0 / 0.06);
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.06), 0 4px 6px -4px rgb(0 0 0 / 0.06);
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.06), 0 8px 10px -6px rgb(0 0 0 / 0.06);
+ --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.15);
+ --tracking-normal: normal;
+}
+
+html {
+ color-scheme: light;
+}
+
+html.dark {
+ color-scheme: dark;
+}
+
+body {
+ @apply bg-background text-foreground antialiased;
+}
+
+@theme inline {
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --radius-2xl: calc(var(--radius) + 8px);
+ --radius-3xl: calc(var(--radius) + 12px);
+ --radius-4xl: calc(var(--radius) + 16px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+ --font-sans: DM Sans, sans-serif;
+ --font-mono: Space Mono, monospace;
+ --font-serif: DM Sans, sans-serif;
+ --radius: 0.75rem;
+ --tracking-tighter: calc(var(--tracking-normal) - 0.05em);
+ --tracking-tight: calc(var(--tracking-normal) - 0.025em);
+ --tracking-wide: calc(var(--tracking-normal) + 0.025em);
+ --tracking-wider: calc(var(--tracking-normal) + 0.05em);
+ --tracking-widest: calc(var(--tracking-normal) + 0.1em);
+ --tracking-normal: var(--tracking-normal);
+ --shadow-2xl: var(--shadow-2xl);
+ --shadow-xl: var(--shadow-xl);
+ --shadow-lg: var(--shadow-lg);
+ --shadow-md: var(--shadow-md);
+ --shadow: var(--shadow);
+ --shadow-sm: var(--shadow-sm);
+ --shadow-xs: var(--shadow-xs);
+ --shadow-2xs: var(--shadow-2xs);
+ --spacing: var(--spacing);
+ --letter-spacing: var(--letter-spacing);
+ --shadow-offset-y: var(--shadow-offset-y);
+ --shadow-offset-x: var(--shadow-offset-x);
+ --shadow-spread: var(--shadow-spread);
+ --shadow-blur: var(--shadow-blur);
+ --shadow-opacity: var(--shadow-opacity);
+ --color-shadow-color: var(--shadow-color);
+ --color-destructive-foreground: var(--destructive-foreground);
+}
+
+.dark {
+ --background: oklch(0.13 0.015 260);
+ --foreground: oklch(0.97 0.005 260);
+ --card: oklch(0.18 0.015 260);
+ --card-foreground: oklch(0.97 0.005 260);
+ --popover: oklch(0.18 0.015 260);
+ --popover-foreground: oklch(0.97 0.005 260);
+ --primary: oklch(0.68 0.16 277);
+ --primary-foreground: oklch(1 0 0);
+ --secondary: oklch(0.22 0.015 260);
+ --secondary-foreground: oklch(0.90 0.005 260);
+ --muted: oklch(0.22 0.015 260);
+ --muted-foreground: oklch(0.65 0.01 260);
+ --accent: oklch(0.22 0.015 260);
+ --accent-foreground: oklch(0.90 0.005 260);
+ --destructive: oklch(0.65 0.20 25);
+ --destructive-foreground: oklch(1 0 0);
+ --border: oklch(0.28 0.015 260);
+ --input: oklch(0.28 0.015 260);
+ --ring: oklch(0.68 0.16 277);
+ --chart-1: oklch(0.68 0.16 277);
+ --chart-2: oklch(0.78 0.13 182);
+ --chart-3: oklch(0.88 0.15 92);
+ --chart-4: oklch(0.73 0.18 350);
+ --chart-5: oklch(0.80 0.18 152);
+ --sidebar: oklch(0.13 0.015 260);
+ --sidebar-foreground: oklch(0.97 0.005 260);
+ --sidebar-primary: oklch(0.68 0.16 277);
+ --sidebar-primary-foreground: oklch(1 0 0);
+ --sidebar-accent: oklch(0.22 0.015 260);
+ --sidebar-accent-foreground: oklch(0.90 0.005 260);
+ --sidebar-border: oklch(0.28 0.015 260);
+ --sidebar-ring: oklch(0.68 0.16 277);
+}
+
+@layer base {
+ body {
+ letter-spacing: var(--tracking-normal);
+ }
+}
+
+/* Dot grid background pattern */
+.dot-grid {
+ background-image: radial-gradient(circle, oklch(0.75 0.01 260) 1px, transparent 1px);
+ background-size: 24px 24px;
+}
+
+.dark .dot-grid {
+ background-image: radial-gradient(circle, oklch(0.30 0.01 260) 1px, transparent 1px);
+}
+
+@keyframes fade-up {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes scale-in {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-6px);
+ }
+}
+
+.animate-fade-up {
+ animation: fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.animate-fade-in {
+ animation: fade-in 0.5s ease-out both;
+}
+
+.animate-scale-in {
+ animation: scale-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+
+.animate-float {
+ animation: float 3s ease-in-out infinite;
+}
diff --git a/community/privy-auth/src/app/layout.tsx b/community/privy-auth/src/app/layout.tsx
new file mode 100644
index 00000000..f3652077
--- /dev/null
+++ b/community/privy-auth/src/app/layout.tsx
@@ -0,0 +1,40 @@
+import type { Metadata } from "next";
+import { DM_Sans, Space_Mono } from "next/font/google";
+import { ThemeProvider } from "next-themes";
+import { Providers } from "@/components/providers";
+import { Toaster } from "@/components/ui/sonner";
+import "./globals.css";
+
+const dmSans = DM_Sans({
+ subsets: ["latin"],
+ variable: "--font-sans",
+});
+
+const spaceMono = Space_Mono({
+ weight: ["400", "700"],
+ subsets: ["latin"],
+ variable: "--font-mono",
+});
+
+export const metadata: Metadata = {
+ title: "privy-auth — Solana Privy Auth Template",
+ description:
+ "Solana dApp starter with Privy authentication, social logins, and embedded wallets",
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/community/privy-auth/src/app/page.tsx b/community/privy-auth/src/app/page.tsx
new file mode 100644
index 00000000..3814ee8d
--- /dev/null
+++ b/community/privy-auth/src/app/page.tsx
@@ -0,0 +1,201 @@
+"use client";
+
+import { usePrivy } from "@privy-io/react-auth";
+import { useRouter } from "next/navigation";
+import { useEffect } from "react";
+import { LoginButton } from "@/components/login-button";
+import { ThemeToggle } from "@/components/theme-toggle";
+import {
+ Wallet,
+ ShieldCheck,
+ Zap,
+ ArrowRight,
+ Chrome,
+ MessageCircle,
+} from "lucide-react";
+
+const features = [
+ {
+ icon: Chrome,
+ title: "Google",
+ desc: "One-tap sign in",
+ },
+ {
+ icon: MessageCircle,
+ title: "Discord",
+ desc: "Community login",
+ },
+ {
+ icon: Zap,
+ title: "Twitter",
+ desc: "Social auth",
+ },
+ {
+ icon: Wallet,
+ title: "Wallet",
+ desc: "Phantom, Solflare & more",
+ },
+];
+
+const highlights = [
+ {
+ icon: Wallet,
+ title: "Embedded Wallets",
+ desc: "Auto-created Solana wallet for users without one",
+ },
+ {
+ icon: ShieldCheck,
+ title: "Protected Routes",
+ desc: "Middleware and client-side auth guards",
+ },
+ {
+ icon: Zap,
+ title: "Instant Setup",
+ desc: "Drop-in Privy provider, ready to build",
+ },
+];
+
+export default function Home() {
+ const { ready, authenticated } = usePrivy();
+ const router = useRouter();
+
+ useEffect(() => {
+ if (ready && authenticated) {
+ router.replace("/dashboard");
+ }
+ }, [ready, authenticated, router]);
+
+ return (
+
+ {/* Dot grid background */}
+
+
+ {/* Top nav */}
+
+
+ {/* Hero */}
+
+ {/* Gradient orb */}
+
+
+
+ {/* Badge */}
+
+
+
+ Solana + Privy Auth Template
+
+
+
+ {/* Headline */}
+
+
+ Auth that just{" "}
+ works
+
+
+ Social logins, embedded wallets, and protected routes for your
+ Solana dApp. Built with Privy.
+
+
+
+ {/* Login methods grid */}
+
+
+ {features.map((item) => (
+
+
+
+
+
+
{item.title}
+
+ {item.desc}
+
+
+
+ ))}
+
+
+
+ {/* CTA */}
+
+ {ready && !authenticated && (
+
+
+
+ )}
+
+ {!ready && (
+
+
+
+ Initializing...
+
+
+ )}
+
+
+ {/* Highlights */}
+
+
+
+ {highlights.map((item) => (
+
+
+
+
+
+
{item.title}
+
+ {item.desc}
+
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/community/privy-auth/src/components/login-button.tsx b/community/privy-auth/src/components/login-button.tsx
new file mode 100644
index 00000000..e1c9e4ff
--- /dev/null
+++ b/community/privy-auth/src/components/login-button.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import { useLogin } from "@privy-io/react-auth";
+import { useRouter } from "next/navigation";
+import { Button } from "@/components/ui/button";
+import { ArrowRight } from "lucide-react";
+
+export function LoginButton() {
+ const router = useRouter();
+
+ const { login } = useLogin({
+ onComplete: ({ user }) => {
+ if (user) {
+ router.replace("/dashboard");
+ }
+ },
+ onError: (error) => {
+ console.error("Login failed:", error);
+ },
+ });
+
+ return (
+
+ );
+}
diff --git a/community/privy-auth/src/components/providers.tsx b/community/privy-auth/src/components/providers.tsx
new file mode 100644
index 00000000..923c1ab0
--- /dev/null
+++ b/community/privy-auth/src/components/providers.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { PrivyProvider } from "@privy-io/react-auth";
+import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
+
+const solanaConnectors = toSolanaWalletConnectors({
+ shouldAutoConnect: true,
+});
+
+export function Providers({ children }: { children: React.ReactNode }) {
+ const { resolvedTheme } = useTheme();
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/community/privy-auth/src/components/sign-message.tsx b/community/privy-auth/src/components/sign-message.tsx
new file mode 100644
index 00000000..6253af0f
--- /dev/null
+++ b/community/privy-auth/src/components/sign-message.tsx
@@ -0,0 +1,102 @@
+"use client";
+
+import { useState } from "react";
+import { useWallets, useSignMessage } from "@privy-io/react-auth/solana";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { toast } from "sonner";
+import { PenLine, Copy, Loader2 } from "lucide-react";
+
+export function SignMessage() {
+ const { wallets } = useWallets();
+ const { signMessage } = useSignMessage();
+ const embedded = wallets.find((w) => w.standardWallet.name === "Privy") ?? null;
+ const [signature, setSignature] = useState(null);
+ const [loading, setLoading] = useState(false);
+
+ const handleSign = async () => {
+ if (!embedded) return;
+
+ setLoading(true);
+ setSignature(null);
+
+ try {
+ const message = new TextEncoder().encode(
+ "Hello from privy-auth! This is a demo message signed with your embedded Solana wallet.",
+ );
+
+ const { signature: result } = await signMessage({
+ message,
+ wallet: embedded,
+ });
+
+ const sig = Buffer.from(result).toString("base64");
+ setSignature(sig);
+ toast.success("Message signed!");
+ } catch (err) {
+ toast.error(
+ err instanceof Error ? err.message : "Failed to sign message",
+ );
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+ Sign Message
+
+ {embedded
+ ? "Sign a demo message with your embedded wallet."
+ : "Waiting for embedded wallet..."}
+
+
+
+
+
+
+
+ {signature && (
+
+
+ Signature
+
+
+
+ )}
+
+
+ );
+}
diff --git a/community/privy-auth/src/components/theme-toggle.tsx b/community/privy-auth/src/components/theme-toggle.tsx
new file mode 100644
index 00000000..91b51c5a
--- /dev/null
+++ b/community/privy-auth/src/components/theme-toggle.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { useEffect, useState } from "react";
+import { Moon, Sun } from "lucide-react";
+import { Button } from "@/components/ui/button";
+
+export function ThemeToggle() {
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => setMounted(true), []);
+
+ if (!mounted) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/community/privy-auth/src/components/ui/badge.tsx b/community/privy-auth/src/components/ui/badge.tsx
new file mode 100644
index 00000000..e87d62bf
--- /dev/null
+++ b/community/privy-auth/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/community/privy-auth/src/components/ui/button.tsx b/community/privy-auth/src/components/ui/button.tsx
new file mode 100644
index 00000000..65d4fcd9
--- /dev/null
+++ b/community/privy-auth/src/components/ui/button.tsx
@@ -0,0 +1,57 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/community/privy-auth/src/components/ui/card.tsx b/community/privy-auth/src/components/ui/card.tsx
new file mode 100644
index 00000000..cabfbfc5
--- /dev/null
+++ b/community/privy-auth/src/components/ui/card.tsx
@@ -0,0 +1,76 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/community/privy-auth/src/components/ui/separator.tsx b/community/privy-auth/src/components/ui/separator.tsx
new file mode 100644
index 00000000..12d81c4a
--- /dev/null
+++ b/community/privy-auth/src/components/ui/separator.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import * as React from "react"
+import * as SeparatorPrimitive from "@radix-ui/react-separator"
+
+import { cn } from "@/lib/utils"
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = "horizontal", decorative = true, ...props },
+ ref
+ ) => (
+
+ )
+)
+Separator.displayName = SeparatorPrimitive.Root.displayName
+
+export { Separator }
diff --git a/community/privy-auth/src/components/ui/sonner.tsx b/community/privy-auth/src/components/ui/sonner.tsx
new file mode 100644
index 00000000..452f4d9f
--- /dev/null
+++ b/community/privy-auth/src/components/ui/sonner.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import { useTheme } from "next-themes"
+import { Toaster as Sonner } from "sonner"
+
+type ToasterProps = React.ComponentProps
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+
+ )
+}
+
+export { Toaster }
diff --git a/community/privy-auth/src/components/ui/tooltip.tsx b/community/privy-auth/src/components/ui/tooltip.tsx
new file mode 100644
index 00000000..28e19183
--- /dev/null
+++ b/community/privy-auth/src/components/ui/tooltip.tsx
@@ -0,0 +1,32 @@
+"use client"
+
+import * as React from "react"
+import * as TooltipPrimitive from "@radix-ui/react-tooltip"
+
+import { cn } from "@/lib/utils"
+
+const TooltipProvider = TooltipPrimitive.Provider
+
+const Tooltip = TooltipPrimitive.Root
+
+const TooltipTrigger = TooltipPrimitive.Trigger
+
+const TooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+TooltipContent.displayName = TooltipPrimitive.Content.displayName
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
diff --git a/community/privy-auth/src/components/user-profile.tsx b/community/privy-auth/src/components/user-profile.tsx
new file mode 100644
index 00000000..f58b0cd8
--- /dev/null
+++ b/community/privy-auth/src/components/user-profile.tsx
@@ -0,0 +1,131 @@
+"use client";
+
+import { usePrivy } from "@privy-io/react-auth";
+import { useWallets } from "@privy-io/react-auth/solana";
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Separator } from "@/components/ui/separator";
+import { toast } from "sonner";
+import { Copy, LogOut, User } from "lucide-react";
+
+function truncate(str: string) {
+ return `${str.slice(0, 6)}...${str.slice(-4)}`;
+}
+
+export function UserProfile() {
+ const { user, logout } = usePrivy();
+ const { wallets } = useWallets();
+ const embedded = wallets.find((w) => w.standardWallet.name === "Privy") ?? null;
+ const external = wallets.filter((w) => w.standardWallet.name !== "Privy");
+
+ if (!user) return null;
+
+ const linkedMethods = user.linkedAccounts
+ .map((a) => a.type)
+ .filter((t) => t !== "wallet" && t !== "smart_wallet");
+
+ return (
+
+
+
+
+
+
+
+
+ User ID
+
+
{truncate(user.id)}
+
+
+ {embedded && (
+ <>
+
+
+
+ Embedded Wallet
+
+
+
+ >
+ )}
+
+ {external.length > 0 && (
+ <>
+
+
+
+ External Wallets
+
+
+ {external.map((w) => (
+
+
+ {truncate(w.address)}
+
+
+ {w.standardWallet.name}
+
+
+ ))}
+
+
+ >
+ )}
+
+ {linkedMethods.length > 0 && (
+ <>
+
+
+
+ Linked Accounts
+
+
+ {linkedMethods.map((method) => (
+
+ {method.replace("_oauth", "")}
+
+ ))}
+
+
+ >
+ )}
+
+
+ );
+}
diff --git a/community/privy-auth/src/components/wallet-info.tsx b/community/privy-auth/src/components/wallet-info.tsx
new file mode 100644
index 00000000..cec5830d
--- /dev/null
+++ b/community/privy-auth/src/components/wallet-info.tsx
@@ -0,0 +1,73 @@
+"use client";
+
+import { usePrivy } from "@privy-io/react-auth";
+import { useWallets } from "@privy-io/react-auth/solana";
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Activity } from "lucide-react";
+
+function StatusDot({ active }: { active: boolean }) {
+ return (
+
+ {active && (
+
+ )}
+
+
+ );
+}
+
+export function WalletInfo() {
+ const { authenticated } = usePrivy();
+ const { wallets, ready } = useWallets();
+ const embedded = wallets.find((w) => w.standardWallet.name === "Privy") ?? null;
+
+ if (!ready) {
+ return (
+
+
+ Initializing...
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {authenticated ? "Authenticated" : "Not authenticated"}
+
+
+
+
+
+ {embedded ? "Embedded wallet active" : "No embedded wallet"}
+
+
+
+ 0} />
+
+ {wallets.length} wallet{wallets.length !== 1 ? "s" : ""} connected
+
+
+
+
+ );
+}
diff --git a/community/privy-auth/src/lib/utils.ts b/community/privy-auth/src/lib/utils.ts
new file mode 100644
index 00000000..bd0c391d
--- /dev/null
+++ b/community/privy-auth/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/community/privy-auth/tailwind.config.ts b/community/privy-auth/tailwind.config.ts
new file mode 100644
index 00000000..76f9ee39
--- /dev/null
+++ b/community/privy-auth/tailwind.config.ts
@@ -0,0 +1,11 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ content: ["./src/**/*.{ts,tsx}"],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+};
+
+export default config;
diff --git a/community/privy-auth/tsconfig.json b/community/privy-auth/tsconfig.json
new file mode 100644
index 00000000..b8cdcc79
--- /dev/null
+++ b/community/privy-auth/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}