From frontend-tools
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"
How this skill is triggered — by the user, by Claude, or both
Slash command
/frontend-tools:firebase-nextjs-integration-strategiesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
Use For:
onSnapshot)Location: src/lib/firebase/client.ts
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);
Use For:
Location: src/lib/firebase/admin.ts
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);
Advantages:
// 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 (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</div>
);
}
Use When:
// components/CommentsList.tsx
'use client';
import { useEffect, useState } from 'react';
import { collection, query, where, onSnapshot, orderBy } from 'firebase/firestore';
import { db } from '@/lib/firebase/client';
import { commentConverter, type Comment } from '@/lib/firebase/schemas/comment.schema';
export function CommentsList({ postId }: { postId: string }) {
const [comments, setComments] = useState<Comment[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Real-time listener with withConverter
const q = query(
collection(db, 'comments'),
where('postId', '==', postId),
orderBy('createdAt', 'desc')
).withConverter(commentConverter);
const unsubscribe = onSnapshot(q, (snapshot) => {
const commentsData = snapshot.docs.map(doc => doc.data());
setComments(commentsData);
setLoading(false);
});
return unsubscribe; // Cleanup on unmount
}, [postId]);
if (loading) return <div>Loading comments...</div>;
return (
<div>
{comments.map(comment => (
<div key={comment.id}>{comment.content}</div>
))}
</div>
);
}
Use For:
// app/actions/createPost.ts
'use server';
import { adminDb } from '@/lib/firebase/admin';
import { PostSchema, postAdminConverter } from '@/lib/firebase/schemas/post.schema';
import { revalidatePath } from 'next/cache';
import { z } from 'zod';
const CreatePostInput = PostSchema.pick({
title: true,
content: true,
}).extend({
authorId: z.string(),
});
export async function createPost(input: z.infer<typeof CreatePostInput>) {
// Validate input
const validated = CreatePostInput.parse(input);
// Create post with Admin SDK
const postRef = adminDb.collection('posts').doc();
await postRef.withConverter(postAdminConverter).set({
...validated,
status: 'draft',
viewCount: 0,
createdAt: new Date(),
updatedAt: new Date(),
});
// Revalidate posts page cache
revalidatePath('/posts');
return { success: true, postId: postRef.id };
}
When to use Server Components:
When to use Client Components:
When to use Server Actions:
When to use API Routes:
Do:
HttpOnly cookies (not localStorage)server-only package for admin filesDon't:
onSnapshot)Related Skills: firebase-admin-sdk-server-integration, firestore-data-modeling-patterns
npx claudepluginhub agentient/vibekit --plugin frontend-toolsGuides Firebase backend development covering auth, Firestore, Realtime DB, Cloud Functions, Storage, Hosting; emphasizes security rules, denormalized data modeling, and query optimization.
Provides Next.js App Router data fetching patterns: server/client fetching, SWR/React Query integration, parallel fetches, ISR, revalidation, error boundaries. Use for caching strategies and loading/error states.
Covers Next.js 14+ App Router patterns: RSC, ISR, middleware, parallel routes, and data fetching with practical examples.