
neon-auth-nextjs
Sets up Neon Auth in Next.js App Router applications. Configures API routes, middleware, server components, and UI. Use when adding auth-only to Next.js apps (no database needed).
- Installs
- 56
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/neondatabase/neon-js --skill neon-auth-nextjs- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads neon-auth-nextjs automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Neon Auth for Next.js
Help developers set up @neondatabase/auth in Next.js App Router applications (auth only, no database).
When to Use
Use this skill when:
- Setting up Neon Auth in Next.js (App Router)
- User mentions "next.js", "next", or "app router" with Neon Auth
- Auth-only setup (no database needed)
Critical Rules
- Server vs Client imports: Use correct import paths
'use client'directive: Required for client components using hooks- CSS Import: Choose ONE - either
/ui/cssOR/ui/tailwind, never both - onSessionChange: Always call
router.refresh()to update Server Components
Critical Imports
| Purpose | Import From |
|---|---|
Unified Server (createNeonAuth) | @neondatabase/auth/next/server |
| Client Auth | @neondatabase/auth/next |
| UI Components | @neondatabase/auth/react/ui |
| View Paths (static params) | @neondatabase/auth/react/ui/server |
Note: Use createNeonAuth() from @neondatabase/auth/next/server to get a unified auth instance that provides:
.handler()- API route handler.middleware()- Route protection middleware- All Better Auth server methods (
.signIn,.signUp,.getSession, etc.)
Setup
1. Install
npm install @neondatabase/auth
2. Environment (.env.local)
NEON_AUTH_BASE_URL=https://your-auth.neon.tech
NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long
Important: Generate a secure secret (32+ characters) for production:
openssl rand -base64 32
3. Server Setup (lib/auth/server.ts)
Create a auth instance that provides handler, middleware, and server methods:
import { createNeonAuth } from '@neondatabase/auth/next/server';
export const auth = createNeonAuth({
baseUrl: process.env.NEON_AUTH_BASE_URL!,
cookies: {
secret: process.env.NEON_AUTH_COOKIE_SECRET!,
sessionDataTtl: 300, // Optional: session data cache TTL in seconds (default: 300 = 5 min)
domain: '.example.com', // Optional: for cross-subdomain cookies
},
});
4. API Route (app/api/auth/[...path]/route.ts)
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();
5. Middleware (middleware.ts)
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*'],
};
6. Client (lib/auth/client.ts)
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient();
7. Provider (app/providers.tsx)
'use client';
import { NeonAuthUIProvider } from '@neondatabase/auth/react/ui';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { authClient } from '@/lib/auth/client';
export function Providers({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
<NeonAuthUIProvider
authClient={authClient}
navigate={router.push}
replace={router.replace}
onSessionChange={() => router.refresh()}
redirectTo="/dashboard"
Link={({href, children}) => <Link to={href}>{children}</Link>}
>
{children}
</NeonAuthUIProvider>
);
}
8. Layout (app/layout.tsx)
import { Providers } from './providers';
import '@neondatabase/auth/ui/css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
9. Auth Pages (app/auth/[path]/page.tsx)
import { AuthView } from '@neondatabase/auth/react/ui';
import { authViewPaths } from '@neondatabase/auth/react/ui/server';
export function generateStaticParams() {
return Object.values(authViewPaths).map((path) => ({ path }));
}
export default async function AuthPage({ params }: { params: Promise<{ path: string }> }) {
const { path } = await params;
return <AuthView pathname={path} />;
}
CSS & Styling
Import Options
Without Tailwind (pre-built CSS bundle ~47KB):
// app/layout.tsx
import '@neondatabase/auth/ui/css';
With Tailwind CSS v4 (app/globals.css):
@import 'tailwindcss';
@import '@neondatabase/auth/ui/tailwind';
IMPORTANT: Never import both - causes duplicate styles.
Dark Mode
The provider includes next-themes. Control via defaultTheme prop:
<NeonAuthUIProvider
defaultTheme="system" // 'light' | 'dark' | 'system'
// ...
>
Custom Theming
Override CSS variables in globals.css:
:root {
--primary: hsl(221.2 83.2% 53.3%);
--primary-foreground: hsl(210 40% 98%);
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(222.2 84% 4.9%);
--border: hsl(214.3 31.8% 91.4%);
--input: hsl(214.3 31.8% 91.4%);
--ring: hsl(221.2 83.2% 53.3%);
--radius: 0.5rem;
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
/* ... dark mode overrides */
}
NeonAuthUIProvider Props
Full configuration options:
<NeonAuthUIProvider
// Required
authClient={authClient}
// Navigation (Next.js specific)
navigate={router.push} // router.push for navigation
replace={router.replace} // router.replace for redirects
onSessionChange={() => router.refresh()} // Refresh Server Components!
redirectTo="/dashboard" // Where to redirect after auth
Link={({href, children}) => <Link to={href}>{children}</Link>} // Next.js Link component
// Social/OAuth Providers
social={{
providers: ['google'],
}}
// Feature Flags
emailOTP={true} // Enable email OTP sign-in
emailVerification={true} // Require email verification
magicLink={false} // Magic link (disabled by default)
multiSession={false} // Multiple sessions (disabled)
// Credentials Configuration
credentials={{
forgotPassword: true, // Show forgot password link
}}
// Sign Up Fields
signUp={{
fields: ['name'], // Additional fields: 'name', 'username', etc.
}}
// Account Settings Fields
account={{
fields: ['image', 'name', 'company', 'age', 'newsletter'],
}}
// Organization Features
organization={{}} // Enable org features
// Dark Mode
defaultTheme="system" // 'light' | 'dark' | 'system'
// Custom Labels
localization={{
SIGN_IN: 'Welcome Back',
SIGN_UP: 'Create Account',
FORGOT_PASSWORD: 'Forgot Password?',
OR_CONTINUE_WITH: 'or continue with',
}}
>
{children}
</NeonAuthUIProvider>
Server Components (RSC)
Get Session in Server Component
// NO 'use client' - this is a Server Component
import { auth } from '@/lib/auth/server';
// Server components using `auth` methods must be rendered dynamically
export const dynamic = 'force-dynamic'
export async function Profile() {
const { data: session } = await auth.getSession();
if (!session?.user) return <div>Not signed in</div>;
return (
<div>
<p>Hello, {session.user.name}</p>
<p>Email: {session.user.email}</p>
</div>
);
}
Route Handler with Auth
// app/api/user/route.ts
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { data: session } = await auth.getSession();
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({ user: session.user });
}
Server Actions
Server actions use the same auth instance from lib/auth/server.ts:
Sign In Action
// app/actions/auth.ts
'use server';
import { auth } from '@/lib/auth/server';
import { redirect } from 'next/navigation';
export async function signIn(formData: FormData) {
const { error } = await auth.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signUp(formData: FormData) {
const { error } = await auth.signUp.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
name: formData.get('name') as string,
});
if (error) {
return { error: error.message };
}
redirect('/dashboard');
}
export async function signOut() {
await auth.signOut();
redirect('/');
}
Available Server Methods
The auth instance from createNeonAuth() provides all Better Auth server methods:
// Authentication
auth.signIn.email({ email, password })
auth.signUp.email({ email, password, name })
auth.signOut()
auth.getSession()
// User Management
auth.updateUser({ name, image })
// Organizations
auth.organization.create({ name, slug })
auth.organization.list()
// Admin (if enabled)
auth.admin.listUsers()
auth.admin.banUser({ userId })
Client Components
Session Hook
'use client';
import { authClient } from '@/lib/auth/client';
export function Dashboard() {
const { data: session, isPending, error } = authClient.useSession();
if (isPending) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!session) return <div>Not signed in</div>;
return <div>Hello, {session.user.name}</div>;
}
Client-Side Auth Methods
'use client';
import { authClient } from '@/lib/auth/client';
// Sign in
await authClient.signIn.email({ email, password });
// Sign up
await authClient.signUp.email({ email, password, name });
// OAuth
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});
// Sign out
await authClient.signOut();
// Get session
const session = await authClient.getSession();
UI Components
AuthView - Main Auth Interface
import { AuthView } from '@neondatabase/auth/react/ui';
// Handles: sign-in, sign-up, forgot-password, reset-password, callback, sign-out
<AuthView pathname={path} />
Conditional Rendering
import {
SignedIn,
SignedOut,
AuthLoading,
RedirectToSignIn,
} from '@neondatabase/auth/react/ui';
function MyPage() {
return (
<>
<AuthLoading>
<LoadingSpinner />
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<LandingPage />
</SignedOut>
{/* Auto-redirect if not signed in */}
<RedirectToSignIn />
</>
);
}
UserButton
import { UserButton } from '@neondatabase/auth/react/ui';
function Header() {
return (
<header>
<nav>...</nav>
<UserButton />
</header>
);
}
Account Management
import {
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
ChangeEmailCard,
DeleteAccountCard,
ProvidersCard,
} from '@neondatabase/auth/react/ui';
Organization Components
import {
OrganizationSwitcher,
OrganizationSettingsCards,
OrganizationMembersCard,
AcceptInvitationCard,
} from '@neondatabase/auth/react/ui';
Social/OAuth Providers
Configuration
<NeonAuthUIProvider
social={{
providers: ['google'],
}}
>
Programmatic OAuth
// Client-side
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});
Supported Providers
google, github, twitter, discord, apple, microsoft, facebook, linkedin, spotify, twitch, gitlab, bitbucket
Middleware Configuration
Basic Protected Routes
import { auth } from '@/lib/auth/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*', '/settings/:path*'],
};
Custom Logic
import { auth } from '@/lib/auth/server';
import { NextResponse } from 'next/server';
export default auth.middleware({
loginUrl: '/auth/sign-in',
});
Account Pages Setup
Account Layout (app/account/[path]/page.tsx)
import {
SignedIn,
RedirectToSignIn,
AccountSettingsCards,
SecuritySettingsCards,
SessionsCard,
ChangePasswordCard,
} from '@neondatabase/auth/react/ui';
export default async function AccountPage({ params }: { params: Promise<{ path: string }> }) {
const { path = 'settings' } = await params;
return (
<>
<RedirectToSignIn />
<SignedIn>
{path === 'settings' && <AccountSettingsCards />}
{path === 'security' && (
<>
<ChangePasswordCard />
<SecuritySettingsCards />
</>
)}
{path === 'sessions' && <SessionsCard />}
</SignedIn>
</>
);
}
Advanced Features
Anonymous Access
Enable RLS-based data access for unauthenticated users:
// lib/auth/client.ts
'use client';
import { createAuthClient } from '@neondatabase/auth/next';
export const authClient = createAuthClient({
allowAnonymous: true,
});
Get JWT Token
const token = await authClient.getJWTToken();
// Use in API calls
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
Cross-Tab Sync
Automatic via BroadcastChannel. Sign out in one tab signs out all tabs.
Session Refresh in Server Components
The onSessionChange callback is crucial for Next.js:
<NeonAuthUIProvider
onSessionChange={() => router.refresh()} // Refreshes Server Components!
// ...
>
Without this, Server Components won't update after sign-in/sign-out.
Error Handling
Server Actions
'use server';
export async function signIn(formData: FormData) {
const { error } = await authServer.signIn.email({
email: formData.get('email') as string,
password: formData.get('password') as string,
});
if (error) {
// Return error to client
return { error: error.message };
}
redirect('/dashboard');
}
Client Components
'use client';
const { error } = await authClient.signIn.email({ email, password });
if (error) {
toast.error(error.message);
}
Common Errors
| Error | Cause |
|---|---|
Invalid credentials | Wrong email/password |
User already exists | Email already registered |
Email not verified | Verification required |
Session not found | Expired or invalid session |
FAQ / Troubleshooting
Server Components not updating after sign-in?
Make sure you have onSessionChange={() => router.refresh()} in your provider:
<NeonAuthUIProvider
onSessionChange={() => router.refresh()}
// ...
>
Anonymous access not working?
Grant permissions to the anonymous role in your database:
GRANT SELECT ON public.posts TO anonymous;
GRANT SELECT ON public.products TO anonymous;
And configure RLS policies:
CREATE POLICY "Anyone can read published posts"
ON public.posts FOR SELECT
USING (published = true);
Middleware not protecting routes?
Check your matcher configuration:
export const config = {
matcher: [
'/dashboard/:path*',
'/account/:path*',
// Add your protected routes here
],
};
OAuth callback errors?
Ensure your API route is set up correctly at app/api/auth/[...path]/route.ts:
import { auth } from '@/lib/auth/server';
export const { GET, POST } = auth.handler();
Session not persisting?
- Check cookies are enabled
- Verify
NEON_AUTH_BASE_URLis correct in.env.local - Verify
NEON_AUTH_COOKIE_SECRETis set and at least 32 characters - Make sure you're not in incognito with cookies blocked
Session data cache not working?
- Verify
NEON_AUTH_COOKIE_SECRETis at least 32 characters long - Check
cookies.secretis passed tocreateNeonAuth() - Optionally configure
cookies.sessionDataTtl(default: 300 seconds)
Performance Notes
- Session data caching: JWT-signed
session_datacookie with configurable TTL (default: 5 minutes)- Configure via
cookies.sessionDataTtlin seconds - Enables sub-millisecond session lookups (<1ms)
- Automatic fallback to upstream
/get-sessionon cache miss
- Configure via
- Request deduplication: Concurrent calls share single network request (10x faster cold starts)
- Server Components: Use
auth.getSession()for zero-JS session access - Cross-tab sync: <50ms via BroadcastChannel
- Cookie domain: Optional
cookies.domainfor cross-subdomain cookie sharing
FAQ
What does the neon-auth-nextjs skill do?
Sets up Neon Auth in Next.js App Router applications. Configures API routes, middleware, server components, and UI. Use when adding auth-only to Next.js apps (no database needed).
How do I install neon-auth-nextjs?
Run npx skills add https://github.com/neondatabase/neon-js --skill neon-auth-nextjs in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/neon-auth-nextjs/ (personal) or .claude/skills/neon-auth-nextjs/ (project).
Which AI agents can use neon-auth-nextjs?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is neon-auth-nextjs safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains neon-auth-nextjs?
It is published by neondatabase in neondatabase/neon-js, and it is an official skill from the maker of the technology it covers. It has 56 recorded installs.
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.