A comprehensive, reusable SvelteKit blog system with Firebase backend, featuring comments, likes, authentication, and admin tools.
npm install @crashnaut/sveltekit-firebase-blog firebase
// src/lib/firebase.ts
import { initializeBlogFirebase } from '@crashnaut/sveltekit-firebase-blog/firebase';
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "your-app-id"
};
// Initialize the blog library with your Firebase config
initializeBlogFirebase(firebaseConfig);
<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
import { BlogList } from '@crashnaut/sveltekit-firebase-blog';
import { getBlogPosts } from '@crashnaut/sveltekit-firebase-blog/firebase';
import { onMount } from 'svelte';
let posts = [];
let loading = true;
onMount(async () => {
try {
const result = await getBlogPosts();
posts = result.posts;
} catch (error) {
console.error('Error loading posts:', error);
} finally {
loading = false;
}
});
function handleAuthModal(returnUrl: string) {
// Implement your auth modal logic
console.log('Open auth modal, return to:', returnUrl);
}
</script>
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold mb-8">Blog</h1>
{#if loading}
<div class="flex justify-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
{:else}
<BlogList {posts} {handleAuthModal} />
{/if}
</div>
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import { page } from '$app/stores';
import { BlogPost, Comments, LikeButton } from '@crashnaut/sveltekit-firebase-blog';
import { getBlogPost } from '@crashnaut/sveltekit-firebase-blog/firebase';
import { onMount } from 'svelte';
let post = null;
let loading = true;
onMount(async () => {
try {
const slug = $page.params.slug;
post = await getBlogPost(slug);
} catch (error) {
console.error('Error loading post:', error);
} finally {
loading = false;
}
});
function handleAuthModal(returnUrl: string) {
// Implement your auth modal logic
}
</script>
<div class="container mx-auto px-4 py-8">
{#if loading}
<div class="flex justify-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
{:else if post}
<BlogPost {post}>
<svelte:fragment slot="comments">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-semibold">Comments</h3>
<LikeButton postId={post.id} initialLikeCount={post.likeCount} {handleAuthModal} />
</div>
<Comments postId={post.id} {handleAuthModal} />
</svelte:fragment>
</BlogPost>
{:else}
<div class="text-center py-8">
<h1 class="text-2xl font-bold text-gray-900">Post not found</h1>
<p class="text-gray-600 mt-2">The blog post you're looking for doesn't exist.</p>
</div>
{/if}
</div>
// src/lib/stores/auth.ts
import { user } from '@crashnaut/sveltekit-firebase-blog/stores';
import { initializeAuth } from '@crashnaut/sveltekit-firebase-blog/firebase';
// Initialize auth state management
initializeAuth();
// Export the user store to use in your app
export { user };
Your Firebase project needs these collections:
posts
- Blog postscomments
- Comments on postspostLikes
- Post likescommentLikes
- Comment likes/dislikesrules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Blog posts - public read, authenticated write
match /posts/{postId} {
allow read: if true;
allow write: if request.auth != null;
}
// Comments - public read, authenticated write
match /comments/{commentId} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if request.auth != null &&
(request.auth.uid == resource.data.userId);
}
// Post likes - authenticated users only
match /postLikes/{likeId} {
allow read, write: if request.auth != null;
}
// Comment likes - authenticated users only
match /commentLikes/{likeId} {
allow read, write: if request.auth != null;
}
}
}
// Blog management
import {
getBlogPosts,
getBlogPost,
createBlogPost,
updateBlogPost,
deleteBlogPost,
likePost,
unlikePost,
hasUserLikedPost
} from '@crashnaut/sveltekit-firebase-blog/firebase';
// Comments
import {
getComments,
addComment,
deleteComment,
likeComment,
dislikeComment,
hasUserLikedComment,
hasUserDislikedComment
} from '@crashnaut/sveltekit-firebase-blog/firebase';
// Auth
import {
getCurrentUser,
signIn,
signOut,
isAuthenticated
} from '@crashnaut/sveltekit-firebase-blog/firebase';
// Main components
import {
BlogList, // Display list of blog posts
BlogPost, // Display single blog post
Comments, // Comments section
LikeButton // Like/unlike button
} from '@crashnaut/sveltekit-firebase-blog';
import type {
BlogPost,
Comment,
NewComment,
BlogPostsResult
} from '@crashnaut/sveltekit-firebase-blog/types';
Create and manage blog posts from the command line:
npx blog-manager
Features:
Migrate existing markdown files to Firebase:
npx migrate-blogs --verbose
npx migrate-blogs --no-dry-run # Actually migrate
npx migrate-blogs --validate # Validate posts only
The library uses Tailwind CSS classes. Make sure your project has Tailwind configured:
// tailwind.config.js
export default {
content: [
'./src/**/*.{html,js,svelte,ts}',
'./node_modules/@crashnaut/sveltekit-firebase-blog/**/*.{js,svelte,ts}'
],
// ... your other config
}
<script lang="ts">
import { Modal } from 'your-ui-library';
import { signIn } from '@crashnaut/sveltekit-firebase-blog/firebase';
let showAuthModal = false;
let returnUrl = '';
function handleAuthModal(url: string) {
returnUrl = url;
showAuthModal = true;
}
async function handleSignIn() {
try {
await signIn();
showAuthModal = false;
// Optionally redirect to returnUrl
} catch (error) {
console.error('Sign in failed:', error);
}
}
</script>
<BlogList posts={posts} openAuthModal={handleAuthModal} />
<Modal bind:open={showAuthModal}>
<h2>Sign In Required</h2>
<p>Please sign in to interact with posts.</p>
<button on:click={handleSignIn}>Sign In with Google</button>
</Modal>
<script lang="ts">
import { user } from '@crashnaut/sveltekit-firebase-blog/stores';
import { createBlogPost, updateBlogPost, deleteBlogPost } from '@crashnaut/sveltekit-firebase-blog/firebase';
// Only show admin interface to authenticated users
$: isAdmin = $user && $user.email === '[email protected]';
</script>
{#if isAdmin}
<div class="admin-panel">
<!-- Your admin interface here -->
</div>
{/if}
To contribute to this library:
git clone https://github.com/crashnaut/sveltekit-firebase-blog
cd sveltekit-firebase-blog
npm install
npm run dev
MIT © Crashnaut