Personal portfolio and tech blog, migrated from Jekyll to Astro.
Live site: https://pradipta.github.io
- Rebuilt the site in Astro + TypeScript + Tailwind CSS
- Preserved every existing blog post, image, and public URL (
/:slug/) - Added a cleaner layout with Home, Blog, Projects, About, and 404
- Added light/dark theme (system default + manual toggle +
localStorage) - Added SEO essentials: sitemap, robots.txt, RSS, Open Graph, Twitter cards, canonical URLs, JSON-LD
- Added blog UX: reading time, tags, previous/next posts, desktop TOC, syntax highlighting, code copy buttons
- Deployed via GitHub Actions → GitHub Pages
Astro is a strong fit for a content-heavy personal site:
- Ships minimal JavaScript by default (fast by design)
- First-class Markdown content collections with typed frontmatter
- Excellent static output for GitHub Pages
- Built-in image tooling, RSS helpers, and sitemap integrations
- Node.js ≥ 22 LTS (the project
enginesfield requires>=22.12.0) - npm (comes with Node)
Check your versions:
node -v
npm -vgit clone https://github.com/pradipta/pradipta.github.io.git
cd pradipta.github.io
npm installStart the dev server:
npm run devOpen http://localhost:4321 (Astro’s default).
Create a production build:
npm run buildOutput is written to dist/.
Preview the production build locally:
npm run previewThis serves the contents of dist/ (typically at http://localhost:4321).
This repository is a user site (username.github.io), so it deploys to the site root — no base path is required.
- Open the repo on GitHub → Settings → Pages
- Under Build and deployment → Source, choose GitHub Actions
- Ensure the default branch is
masterormain(the workflow listens to both)
The workflow lives at .github/workflows/deploy.yml.
It:
- Runs on push to
main/master(and via manual workflow_dispatch) - Builds with the official
withastro/action - Deploys with
actions/deploy-pages
No extra secrets are required for a public Pages site.
Relevant settings in astro.config.mjs:
export default defineConfig({
site: "https://pradipta.github.io",
trailingSlash: "always",
// no `base` — this is a username.github.io root site
});| Repo type | Example URL | base |
|---|---|---|
| User/org site (this project) | https://pradipta.github.io/ |
omit / / |
| Project site | https://user.github.io/repo/ |
/repo |
Because this repo is pradipta.github.io, assets and routes use root-relative paths (/blog/, /assets/img/...).
- Static assets live in
public/and are copied todist/as-is - Blog cover images remain at
/assets/img/<filename>to preserve old URLs - Favicons are in
public/(favicon.ico, apple-touch icons, etc.)
If you later use a custom domain:
-
Configure DNS at your registrar (A/AAAA or CNAME per GitHub’s docs)
-
Add
public/CNAMEcontaining only your domain, e.g.:example.com -
Update
siteinastro.config.mjsto that domain -
Keep no
basepath for an apex/custom domain on a user site
- Do not add a CNAME file unless you own a custom domain
- If present, GitHub Pages will serve the site on that hostname
Locally you only need:
npm run buildProduction deploys happen automatically when you push to the default branch. You can also run the workflow from the GitHub Actions tab → Deploy to GitHub Pages → Run workflow.
-
Create a Markdown file in
src/content/blog/ -
Use a URL-friendly filename — it becomes the path:
src/content/blog/my-new-post.md → https://pradipta.github.io/my-new-post/ -
Add frontmatter (see Content Guide below)
-
Commit and push — GitHub Actions builds and deploys
Create src/content/blog/your-slug.md:
---
title: "Your Post Title"
date: 2026-07-19
description: "Short summary for SEO and cards."
img: cover-image.png
tags: [Java, Git]
featured: false
draft: false
---
Your content in Markdown…Frontmatter fields:
| Field | Required | Notes |
|---|---|---|
title |
yes | Post title |
date |
yes | Publication date |
description |
no | Meta / card summary |
img |
no | Filename under public/assets/img/ |
figCaption |
no | Caption under the cover image |
tags |
no | Array of strings |
featured |
no | Show on the home Featured section |
draft |
no | When true, excluded from listings/RSS |
-
Put files in
public/assets/img/ -
Reference covers via frontmatter:
img: my-photo.jpg→/assets/img/my-photo.jpg -
Inline in Markdown:

Site-wide metadata lives in src/consts.ts (title, description, social links, author).
Projects listed on Home / Projects are defined in src/data/projects.ts.
Add tags in post frontmatter. Tag index and per-tag pages are generated automatically at /tags/ and /tags/<slug>/.
git add .
git commit -m "Add post: your slug"
git push origin masterGitHub Actions deploys the update. Confirm under Actions and Settings → Pages.
src/
components/ # UI pieces (Header, Footer, PostCard, TOC, …)
content/blog/ # Markdown posts (content collection)
data/ # Typed data (projects)
layouts/ # Base + blog layouts
pages/ # Routes (/, /blog/, /about/, /projects/, /:slug/, …)
styles/ # Global CSS + Tailwind
utils/ # Helpers (reading time, post queries)
public/
assets/img/ # Images + favicons (preserved paths)
robots.txt
.github/workflows/deploy.yml
astro.config.mjs
- Defaults to system preference
- Toggle in the header switches light/dark
- Preference is stored in
localStorageundertheme - An inline boot script on
<html>prevents a flash of the wrong theme
| Command | Description |
|---|---|
npm run dev |
Dev server |
npm run build |
Production build → dist/ |
npm run preview |
Preview production build |
npm run astro |
Astro CLI |
See LICENSE.