Personal portfolio website built with Next.js 15, React 19, TypeScript, and Tailwind CSS. Features a single-page portfolio layout with separate pages for blogs and educational notes (Machine Learning & Reinforcement Learning) rendered from MDX.
npm install
npm run dev
Open http://localhost:3000 in your browser.
npm run build
This generates a static export in the out/ directory, ready for GitHub Pages deployment.
npm run lint
src/
├── app/
│ ├── layout.tsx # Root layout (fonts, metadata, CDN links)
│ ├── page.tsx # Main portfolio page
│ ├── _components/ # Shared UI components
│ │ ├── navbar.tsx # Top navigation bar
│ │ ├── social-links.tsx # Social media icons
│ │ ├── about.tsx # About section
│ │ ├── education.tsx # Education section
│ │ ├── experience.tsx # Experience section
│ │ ├── projects.tsx # Projects section
│ │ ├── publications.tsx # Publications section
│ │ ├── skills.tsx # Skills section
│ │ ├── miscellaneous.tsx # Miscellaneous section
│ │ ├── footer.tsx # Footer
│ │ ├── badge.tsx # Badge component for blog tags
│ │ ├── linksSection.tsx # Numbered links list for notes
│ │ └── markdown/ # MDX rendering components
│ ├── blogs/
│ │ ├── page.tsx # Blogs listing page
│ │ ├── blogsList.tsx # Blog list client component
│ │ └── data.ts # Blog entries data
│ └── notes/
│ ├── page.tsx # Notes grid page
│ ├── notesList.tsx # Notes cards client component
│ ├── layout.tsx # Notes layout wrapper
│ ├── machine-learning/
│ │ ├── page.tsx # ML notes listing
│ │ └── [id]/
│ │ ├── page.tsx # Server component (generateStaticParams)
│ │ ├── client.tsx # Client component (MDX renderer)
│ │ ├── data.ts # ML note entries with lazy imports
│ │ └── notes/ # MDX files for each ML note
│ └── reinforcement-learning/
│ ├── page.tsx # RL notes listing
│ └── [id]/
│ ├── page.tsx # Server component (generateStaticParams)
│ ├── client.tsx # Client component (MDX renderer)
│ ├── data.ts # RL note entries with lazy imports
│ └── notes/ # MDX files for each RL note
├── data/ # Portfolio data files
│ ├── education.ts
│ ├── experience.ts
│ ├── projects.ts
│ ├── publications.ts
│ ├── skills.ts
│ └── miscellaneous.ts
├── styles/
│ └── globals.css # Global styles + Tailwind directives
├── types/
│ └── mdx.d.ts # MDX module type declaration
└── utils.ts # Utility functions (cn)
public/
└── assets/ # Images (profile, logos, project thumbnails)
Open src/app/blogs/data.ts
Add a new entry to the blogs array:
{
title: "Your Blog Title",
description: "A short description of the blog post.",
date: "Month Day, Year", // e.g. "August 20, 2026"
tags: ["tag1", "tag2"], // Tags shown as badges
link: "https://example.com/blog", // External link to the full blog post
}
/blogs page with search and tag filtering.Example: Adding a new Machine Learning note
src/app/notes/machine-learning/[id]/notes/your-note-name.mdx
Write your content in MDX format. You can use KaTeX math with $inline$ and $$block$$ syntax.
src/app/notes/machine-learning/[id]/data.ts:// Add a lazy import at the top of the component array or inline:
{
id: "your-note-id", // URL slug (e.g. "your-note-id" -> /notes/machine-learning/your-note-id)
title: "Your Note Title",
component: lazy(() => import("./notes/your-note-name.mdx")),
}
/notes/machine-learning/notes/machine-learning/your-note-idFor Reinforcement Learning notes, follow the same steps but in the reinforcement-learning directory instead.
src/app/notes/your-category/
├── page.tsx # Category listing page
└── [id]/
├── page.tsx # Server component
├── client.tsx # Client component
├── data.ts # Note entries
└── notes/ # MDX files
page.tsx (category listing) - Use LinksSection to display note links:
import LinksSection from "../../_components/linksSection";
import { data } from "./[id]/data";
export default function YourCategoryPage() {
const links = data.map((note) => ({
title: note.title,
href: `/notes/your-category/${note.id}`,
}));
return <LinksSection title="Your Category" links={links} />;
}
[id]/page.tsx (server component) - Required for static export:
import { data } from "./data";
import YourCategoryNote from "./client";
export function generateStaticParams() {
return data.map((note) => ({ id: note.id }));
}
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
await params;
return <YourCategoryNote />;
}
[id]/client.tsx (client component) - Renders the MDX content:
"use client";
import { useEffect, useMemo } from "react";
import { useParams, useRouter } from "next/navigation";
import MDXSection from "../../../_components/markdown/mdxSection";
import { data } from "./data";
import { ArrowLeft2 } from "iconsax-react";
export default function YourCategoryNote() {
const { id } = useParams();
const router = useRouter();
useEffect(() => {
if (!id || !data.find((note) => note.id === id)) {
router.replace("/notes/your-category");
}
}, [id, router]);
const title = useMemo(() => data.find((note) => note.id === id)?.title, [id]);
const Component = useMemo(() => data.find((note) => note.id === id)?.component, [id]);
return (
<>
<ArrowLeft2 size={20} className="fixed top-4 left-4 text-white opacity-50 hover:opacity-100 cursor-pointer" onClick={() => router.replace("/notes/your-category")} />
<MDXSection title={title ?? "Your Category"}>{Component && <Component />}</MDXSection>
</>
);
}
[id]/data.ts - Define your note entries:
import { lazy } from "react";
export const data = [
{
id: "note-slug",
title: "Note Title",
component: lazy(() => import("./notes/note-slug.mdx")),
},
];
src/app/notes/notesList.tsx to add a card for your new category.All portfolio data is in src/data/. Edit the relevant file:
| Section | File |
|---|---|
| Education | src/data/education.ts |
| Experience | src/data/experience.ts |
| Projects | src/data/projects.ts |
| Publications | src/data/publications.ts |
| Skills | src/data/skills.ts |
| Miscellaneous | src/data/miscellaneous.ts |
Each file exports a typed array. Follow the existing entry format to add new items.
The project is configured for static export with output: "export" in next.config.js.
npm run build to generate the out/ directoryout/ directory contains the full static sitemain branch (or configure GitHub Pages to serve from a specific branch/directory).nojekyll file should be present in the root to prevent GitHub Pages from processing files with Jekyll| Command | Description |
|---|---|
npm run dev |
Start development server (localhost:3000) |
npm run build |
Build static export to out/ |
npm run start |
Start production server |
npm run lint |
Run ESLint |
$...$ for inline math and $$...$$ for display math.public/assets/ and reference them as /assets/filename.png in your code.