{post.title}
{post.content}
--- name: firebase-nextjs-integration-strategies version: "1.0" description: > Next.js 14+ App Router integration with Firebase including server/client SDK separation and data fetching patterns. PROACTIVELY activate for: (1) setting up dual SDK architecture for client/server, (2) fetching data in Server Components with Admin SDK, (3) implementing real-time listeners in Client Components. Triggers: "nextjs firebase", "app router", "server client sdk" group: data core-integration: techniques: primary: ["structured_decomposition"] secondary: [] contracts: input: "none" output: "none" patterns: "none" rubrics: "none" --- # Firebase Next.js Integration Strategies ## Overview Next.js 14+ App Router has a clear server/client boundary. This skill provides patterns for integrating Firebase's dual SDK model (Client SDK + Admin SDK) with Next.js execution environments. ## Dual SDK Architecture ### Client SDK (Client Components, Browser) **Use For**: - Client-side authentication (sign-in, sign-up, sign-out) - Real-time listeners (`onSnapshot`) - Client-side mutations with optimistic updates - File uploads to Storage **Location**: `src/lib/firebase/client.ts` ```typescript import { initializeApp, getApps } from 'firebase/app'; import { getAuth } from 'firebase/auth'; import { getFirestore } from 'firebase/firestore'; import { getStorage } from 'firebase/storage'; const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, }; const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0]; export const auth = getAuth(app); export const db = getFirestore(app); export const storage = getStorage(app); ``` ### Admin SDK (Server Components, API Routes, Server Actions) **Use For**: - Privileged data access in Server Components - Token verification in middleware - Server-side mutations - Bypassing security rules **Location**: `src/lib/firebase/admin.ts` ```typescript import 'server-only'; // CRITICAL: Prevents client bundling import { initializeApp, getApps, cert } from 'firebase-admin/app'; import { getAuth } from 'firebase-admin/auth'; import { getFirestore } from 'firebase-admin/firestore'; const adminConfig = { projectId: process.env.FIREBASE_ADMIN_PROJECT_ID, credential: cert({ projectId: process.env.FIREBASE_ADMIN_PROJECT_ID, clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL, privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY?.replace(/\\n/g, '\n'), }), }; const adminApp = getApps().length === 0 ? initializeApp(adminConfig, 'admin') : getApps()[0]; export const adminAuth = getAuth(adminApp); export const adminDb = getFirestore(adminApp); ``` ## Data Fetching Patterns ### Pattern 1: Server Component (Recommended for Initial Load) **Advantages**: - Fast (no client-side fetch) - SEO-friendly (pre-rendered) - Secure (uses Admin SDK) - Cached by Next.js ```typescript // app/posts/page.tsx import { adminDb } from '@/lib/firebase/admin'; import { postAdminConverter, type Post } from '@/lib/firebase/schemas/post.schema'; export default async function PostsPage() { // Fetch data server-side with Admin SDK const postsSnapshot = await adminDb .collection('posts') .withConverter(postAdminConverter) .where('status', '==', 'published') .orderBy('createdAt', 'desc') .limit(10) .get(); const posts: Post[] = postsSnapshot.docs.map(doc => doc.data()); return (
{post.content}