A modern, responsive personal portfolio website built with Astro, featuring a bento grid home page, full blog system with markdown support, and optimized performance.
- Bento Grid Home: Modern asymmetric grid layout with 9 interactive cards
- Blog System: Full-featured blog with markdown support and Content Collections
- Fully Responsive: Optimized for all devices from mobile to desktop
- SEO Optimized: Complete meta tags, Open Graph, and Twitter Card support
- Fast Performance: Static site generation with Astro for optimal loading speeds
- Dark Mode Support: Automatically adapts to system color scheme preferences
- Accessible: Built with accessibility best practices (WCAG compliant)
- Type-safe: TypeScript and Zod schema validation
- Home: Bento grid layout with introduction, stats, and quick links
- About: Personal background, skills showcase, experience timeline
- Blog: Full blog system with posts, tags, and filtering
- Projects: Portfolio of projects with descriptions and links
- Contact: Contact form and contact information
# 1. Install pnpm globally (if not installed)
npm install -g pnpm
# 2. Install dependencies
pnpm install
# 3. Start development server
pnpm devVisit http://localhost:4321 in your browser.
Home Page - src/pages/index.astro
- Line 16: Change "Your Name" to your actual name
- Line 17: Update your title/role
- Lines 44-49: Update tech stack tags
- Lines 57-62: Update experience stats
- Line 79: Update location
About Page - src/pages/about.astro
- Lines 10-17: Update skills array
- Lines 27-45: Replace bio with your story
- Lines 60-110: Update experience timeline
Contact - src/pages/contact.astro
- Line 130: Update email address
- Line 140: Update phone number
- Line 150: Update location
Edit src/styles/global.css lines 3-5:
--color-primary: #2563eb; /* Your brand color */
--color-secondary: #7c3aed; /* Accent color */Edit src/pages/projects.astro starting at line 7:
- Replace sample projects with your actual projects
- Update titles, descriptions, tags, and links
- Add project images to
public/folder
Create src/content/blog/my-first-post.md:
---
title: "My First Blog Post"
description: "Welcome to my blog!"
pubDate: 2024-03-15
tags: ["introduction"]
---
# Welcome to My Blog
This is my first post. I'll be sharing my thoughts on web development...Update in both:
src/components/Footer.astro- Lines 30-51src/pages/contact.astro- Lines 156-178
# Build for production
pnpm build
# Preview production build
pnpm preview
# Deploy the dist/ folder to:
# - Netlify, Vercel, GitHub Pages, or any static hostThe blog system uses Astro's Content Collections API to:
- Store blog posts as Markdown files in
src/content/blog/ - Automatically generate pages for each post
- Provide type-safe frontmatter validation
- Create tag-based filtering
- Sort posts by publication date
Step 1: Create a Markdown File
Create a new .md file in src/content/blog/:
src/content/blog/my-awesome-post.mdThe filename becomes the URL slug: /blog/my-awesome-post
Step 2: Add Frontmatter
Every blog post requires frontmatter at the top:
---
title: "My Awesome Post"
description: "A brief description for SEO and previews"
pubDate: 2024-03-15
author: "Your Name"
image:
url: "https://example.com/image.jpg"
alt: "Image description"
tags: ["javascript", "tutorial"]
draft: false
---
# Your Content Here
Write your blog post content in Markdown...Step 3: Write Your Content
Use standard Markdown syntax:
## Headings and Subheadings
Regular **bold** and _italic_ text.
- Bullet lists
- Are supported
1. Numbered lists
2. Work too
[Links](https://example.com)

`Inline code` and code blocks:
\`\`\`javascript
const example = 'Hello, World!';
console.log(example);
\`\`\`
> Blockquotes are available| Field | Type | Required | Description |
|---|---|---|---|
title |
string | β Yes | Post title |
description |
string | β Yes | SEO description |
pubDate |
date | β Yes | Publication date (YYYY-MM-DD) |
author |
string | β No | Author name (defaults to 'Your Name') |
updatedDate |
date | β No | Last update date |
image.url |
string | β No | Featured image URL |
image.alt |
string | β No | Image alt text |
tags |
array | β No | Array of tag strings |
draft |
boolean | β No | Hide post if true (default: false) |
The blog automatically generates these pages:
- Blog listing:
/blog - Individual posts:
/blog/{slug}(e.g.,/blog/my-awesome-post) - Tag pages:
/blog/tag/{tagname}(e.g.,/blog/tag/javascript)
Organize your content with tags:
---
tags: ["javascript", "react", "tutorial"]
---- Tag pages are automatically generated
- Clicking a tag shows all posts with that tag
- Tags appear on listing and individual post pages
Hide posts from production:
---
title: "Work in Progress"
draft: true
---Draft posts won't appear in production builds.
1. Use Descriptive Titles
- Good: "10 TypeScript Tips for Better Code Quality"
- Bad: "TypeScript Tips"
2. Write Compelling Descriptions
- Keep descriptions 120-160 characters for optimal SEO
3. Choose Relevant Tags
- Use 2-5 tags per post
- Keep tags consistent across posts
- Use lowercase for better URLs
4. Optimize Images
- Use 1200x630px for featured images
- Include descriptive alt text
- Consider CDN or image optimization
5. Update Dates
---
pubDate: 2024-01-15
updatedDate: 2024-03-20
---Change Post Layout - src/layouts/BlogPostLayout.astro
- Modify header design
- Change typography
- Add author bio
- Include related posts
- Add comments
Modify Blog Listing - src/pages/blog/index.astro
- Change grid layout
- Add search functionality
- Include pagination
- Modify card design
Update Styling - CSS classes:
.prose- Content styling.post-card- Blog card.tag- Tag badges.post-header- Post header
The home page features 9 cards in an asymmetric grid:
- Hero Card (2x2) - Name and introduction
- About Card - Quick bio link
- Tech Stack - Technology tags
- Stats - Experience metrics
- Projects - Work preview
- Location - Where you're based
- Social - Connect buttons
- Quote - Inspirational quote
- Status - Availability indicator
Customize in src/pages/index.astro.
Update:
- Personal bio and story
- Skills array with percentages
- Experience and education timeline
- Core values section
Edit src/pages/about.astro.
Replace sample projects with your work:
- Project data starting at line 7
- Titles, descriptions, and tags
- Demo and GitHub links
- Project images
Edit src/pages/projects.astro.
Update contact information:
- Email, phone, location
- Social media links
- Form behavior (add backend if needed)
Edit src/pages/contact.astro.
All design tokens in src/styles/global.css:
:root {
/* Colors */
--color-primary: #2563eb;
--color-secondary: #7c3aed;
--color-accent: #f59e0b;
/* Spacing */
--spacing-sm: 1rem;
--spacing-md: 1.5rem;
--spacing-lg: 2rem;
/* Other design tokens... */
}- Favicon: Replace
public/favicon.svg - OG Image: Add
public/og-image.jpg(1200x630px) - Touch Icon: Add
public/apple-touch-icon.png(180x180px)
Update in astro.config.mjs:
export default defineConfig({
site: "https://yourdomain.com",
// ...
});Default SEO in src/layouts/BaseLayout.astro.
βββ public/ # Static assets
β βββ favicon.svg
βββ src/
β βββ components/ # Reusable components
β β βββ Header.astro
β β βββ Footer.astro
β βββ content/ # Content Collections
β β βββ config.ts # Schemas
β β βββ blog/ # π Blog posts (Markdown)
β βββ layouts/ # Page layouts
β β βββ BaseLayout.astro
β β βββ BlogPostLayout.astro
β βββ pages/ # Routes
β β βββ index.astro # Home (bento grid)
β β βββ about.astro # About
β β βββ projects.astro # Projects
β β βββ contact.astro # Contact
β β βββ blog/
β β βββ index.astro # Blog listing
β β βββ [slug].astro # Blog posts
β β βββ tag/[tag].astro # Tag pages
β βββ styles/
β βββ global.css
βββ astro.config.mjs
βββ package.json
βββ tsconfig.json
- Astro v4.15 - Static site generator
- Content Collections - Type-safe content management
- TypeScript - Type safety
- Zod - Schema validation
- pnpm - Fast package manager
# Production build
pnpm build
# Preview build locally
pnpm previewNetlify
pnpm build
# Deploy dist/ folderVercel
pnpm build
# Deploy dist/ folderGitHub Pages
- Update
astro.config.mjswith repository name - Add GitHub Actions workflow
- Push to GitHub
Others
Deploy the dist/ folder to any static hosting service.
- Ensure Node.js 18+ is installed
- Delete
node_modules/andpnpm-lock.yaml - Run
pnpm installagain - Check if port 4321 is available
- Clear browser cache
- Restart dev server
- Check browser console for errors
- Run
pnpm install - Run
pnpm buildto generate Content Collection types - Check
tsconfig.json
- Ensure posts are in
src/content/blog/ - Check that
draft: falseor omit draft field - Run
pnpm buildto regenerate types - Verify frontmatter matches schema in
src/content/config.ts
- Create
.astrofile insrc/pages/ - Use
BaseLayout - Add to navigation in
src/components/Header.astro - Add to footer in
src/components/Footer.astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="New Page" description="Description">
<section class="section">
<div class="container">
<h1>New Page</h1>
<p>Your content here</p>
</div>
</section>
</BaseLayout>- Create
.mdfile insrc/content/blog/ - Add required frontmatter
- Write content in Markdown
- Build or restart dev server
Example:
---
title: "Building Better Web Apps"
description: "Tips for modern web development"
pubDate: 2024-03-20
tags: ["web development", "tips"]
image:
url: "/images/blog/web-apps.jpg"
alt: "Web development"
---
# Building Better Web Apps
Content goes here...
## Section 1
More content...Modify src/layouts/BlogPostLayout.astro to calculate and display reading time.
Filter posts by shared tags to show related content at the end of each post.
Integrate a comment system:
- Giscus - GitHub Discussions
- Utterances - GitHub Issues
- Disqus - Traditional comments
Create src/pages/rss.xml.js:
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context) {
const posts = await getCollection("blog");
return rss({
title: "Your Blog",
description: "Your blog description",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/blog/${post.slug}/`,
})),
});
}This project is open source and available under the MIT License.
Running pnpm build generates 16 static pages:
- 4 main pages (Home, About, Projects, Contact)
- 1 blog listing page
- 3 blog post pages
- 8 auto-generated tag filter pages
- β Responsive navigation with mobile menu
- β Bento grid home page (9 interactive cards)
- β Type-safe blog with Content Collections
- β SEO meta tags on all pages
- β Dark mode support
- β Accessibility features
- β Clean, commented code
Ready to launch? Customize the content, update your information, and deploy to production! π