Astro with Firebase
Astro is probably my favorite static site generator. Here’s why:
- Uses npm as a package manager
- Uses JavaScript only where you explicitly ask for it, otherwise just HTML + CSS
- Supports multiple content collections (posts, projects, services, products etc.)
- Multi-framework support: use React, Vue, Svelte, or vanilla JS components together
- File-based routing: Pages map directly to your file structure
- Happy Astro users include Microsoft and Firebase
Besides building my portfolio website with Astro, I’ve also spent some time on building an application that uses both client-side rendering (CSR) for an admin dashboard, and server-side rendering (SSR) for publicly available profiles.
Here are my takeaways.
Getting started with Astro is super easy
npm create astro@latest my-project
cd my-project
npm run dev
# Opens localhost:4321
That’s it. You’ll have a working Astro site in under 2 minutes. The npm create
magic will download the latest version of create-astro
package temporarily, run it immediately, then clean up after itself.
Create custom collections
In content.config.ts
you can define custom collections.
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const projects = defineCollection({
// Load Markdown and MDX files in the `src/content/blog/` directory.
loader: glob({ base: './src/content/projects', pattern: '**/*.{md,mdx}' }),
// Type-check frontmatter using a schema
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
link: z.string().url().optional(),
image: z.string(),
labels: z.array(z.string()), // Array of strings
published: z.boolean().optional(),
}),
});
export const collections = { projects };
Then you can create and edit your collection items in src/content/{collection}
. To create views and template files for these items you can create
/pages/{collection}/index.astro
for main page of the collection/pages/{collection}/[...slug].astro
for items
Collections are ideal for creating entities that share characteristics, like projects, case studies, services, products etc.
Firebase Hosting setup
Setting up Firebase Hosting couldn’t be easier either.
npm install -g firebase-tools
firebase login
cd my-project
firebase init hosting
npm run build
firebase deploy
Server side rendering with Firebase
Let’s say you are building a hybrid application that uses both client-side rendering (CSR) and server side rendering (SSR). This is the case if your app has both a private admin area and a public facing profile area that is to be indexed by search engines.
To achieve this, you want to create public profile pages and populate your Astro page with Firestore data.
You can do this by redirecting the request on a given URL to a Firebase Function, that will query Firestore, generate an HTML page based on an Astro template and return the result.
Firebase Hosting rewrite
In firebase.json
add the following:
{
"hosting": {
"rewrites": [
{
"source": "/@**",
"function": "getProfilePage"
}
]
}
}
This rewrite will catch all URLS that look like your-website.com/@anthony
and redirect the request to the getProfilePage
Firebase Function.
Firebase Function
Inside your function, you can query your Firestore, read your HTML template, and inject the data using placeholders.
const coursePagesRef = firestore.collection('coursePages');
const publicCoursePagesSnapshot = await coursePagesRef
.where('userId', '==', userId)
.where('isPublished', '==', true)
.get();
let publicCoursePagesHtml = '';
if (!publicCoursePagesSnapshot.empty) {
publicCoursePagesSnapshot.forEach(doc => {
const course = doc.data();
publicCoursePagesHtml += `<li><a href="/${course.coursePageId}" target="_blank">${course.courseTitle}</a></li>`;
});
publicCoursePagesHtml = `<ul>${publicCoursePagesHtml}</ul>`;
} else {
publicCoursePagesHtml = '<p>No public courses yet.</p>';
}
// Read the profile.html template
const templatePath = path.join(process.cwd(), 'profile.html');
let profileHtml = fs.readFileSync(templatePath, 'utf8');
// Replace placeholders with actual data
profileHtml = profileHtml.replace('<!-- PROFILE_PAGE_ID -->', profilePageId);
profileHtml = profileHtml.replace('<!-- DISPLAY_NAME -->', displayName);
profileHtml = profileHtml.replace('<!-- PUBLIC_COURSE_PAGES -->', publicCoursePagesHtml);
// Send response
return res.status(200).send(profileHtml);
This way you have successfully integrated Firestore into your Astro page.