Every developer needs a portfolio. But most developer portfolios are built as weekend projects and then forgotten — no SEO, no structured data, no performance optimization, and no content strategy. They sit on a subdomain somewhere, loading slowly, invisible to Google, and doing nothing for the developer's career.
I decided to build manjodh.org differently. My goal was not just a pretty page with my projects — it was a Next.js static site that ranks in search, loads instantly, and serves as a genuine platform for building professional authority. This post covers exactly how I built it, the SEO decisions I made, and the technical architecture behind a Next.js portfolio that actually performs.
I am Manjodh Singh Saran — a full stack developer based in Ludhiana, India. I work as a Senior Software Engineer and Mobile Lead at Truxo.ai, and I have been building web and mobile applications for 4.5 years. This portfolio is both my professional home and an ongoing experiment in developer-focused SEO.
Why Next.js for a Portfolio Site
The first decision was the framework. I chose Next.js 14 with the App Router and static export (output: "export") for several reasons:
Static HTML Generation
For a portfolio and blog, you do not need a server. Static HTML means:
- Zero server costs — Host on Vercel, Netlify, or Cloudflare Pages for free
- Maximum performance — No server-side rendering latency, just pre-built HTML served from a CDN
- Better SEO — Search engines get fully rendered HTML without needing to execute JavaScript
- Reliability — No server to crash, no database to go down
Next.js output: "export" generates a complete static site at build time. Every page becomes an HTML file. Every blog post is pre-rendered. The entire site can be served from a CDN edge node closest to the visitor.
App Router Architecture
The App Router gives me file-system based routing with React Server Components by default. My directory structure is clean:
src/app/
├── page.tsx # Home page
├── layout.tsx # Root layout with metadata
├── blog/
│ ├── page.tsx # Blog listing
│ └── [slug]/
│ └── page.tsx # Individual blog posts
Each page can export its own metadata, which Next.js uses to generate the correct <head> tags. This is critical for SEO — each page gets unique titles, descriptions, and Open Graph tags without any manual HTML manipulation.
React Server Components
Server Components are the default in the App Router. They render on the server (or at build time for static export) and send zero JavaScript to the client. This means my portfolio's HTML shell — the layout, navigation, project cards, and content — adds zero bytes to the client-side JavaScript bundle.
I only use Client Components (marked with "use client") for genuinely interactive elements: the theme toggle, animation triggers, and the mobile navigation menu. Everything else stays as a Server Component.
SEO Architecture: The Technical Details
SEO for a developer portfolio is not about gaming Google. It is about making your content discoverable, properly structured, and fast. Here is every SEO element I implemented.
Meta Tags and Open Graph
Every page on manjodh.org has comprehensive meta tags generated by Next.js metadata API:
export const metadata: Metadata = {
title: "Manjodh Singh Saran - Full Stack Developer",
description: "Full Stack Software Engineer with 4.5 years of experience...",
openGraph: {
title: "Manjodh Singh Saran - Full Stack Developer",
description: "Full Stack Software Engineer...",
url: "https://manjodh.org",
siteName: "Manjodh Singh Saran",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Manjodh Singh Saran - Full Stack Developer",
description: "Full Stack Software Engineer...",
},
};For blog posts, the metadata is generated dynamically from the MDX frontmatter. Each post gets its own title, description, and publication date in the meta tags. This ensures that when someone shares a blog post on LinkedIn or Twitter, it displays with the correct title and description.
Structured Data (JSON-LD)
Structured data is what helps Google understand the content of your pages beyond the visible text. I implemented JSON-LD schemas for:
Person schema on the homepage:
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Manjodh Singh Saran",
"jobTitle": "Senior Software Engineer",
"url": "https://manjodh.org",
"sameAs": [
"https://github.com/ManjodhSaran",
"https://linkedin.com/in/manjodh"
]
}BlogPosting schema on each blog post:
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Post Title",
"datePublished": "2025-03-05",
"author": {
"@type": "Person",
"name": "Manjodh Singh Saran"
}
}These schemas help Google display rich results — author information, publication dates, and other enhanced search features.
Sitemap and Robots.txt
A sitemap tells search engines about every page on your site and when it was last updated. Next.js makes this straightforward with a sitemap.ts file:
export default function sitemap(): MetadataRoute.Sitemap {
const posts = getBlogPosts().map((post) => ({
url: `https://manjodh.org/blog/${post.slug}`,
lastModified: post.metadata.publishedAt,
}));
return [
{ url: "https://manjodh.org", lastModified: new Date() },
{ url: "https://manjodh.org/blog", lastModified: new Date() },
...posts,
];
}The robots.txt allows all crawlers and points them to the sitemap:
User-agent: *
Allow: /
Sitemap: https://manjodh.org/sitemap.xml
Canonical URLs
Every page has a canonical URL to prevent duplicate content issues. This is especially important if your site is accessible via both www and non-www versions, or if you syndicate content elsewhere.
Internal Linking Strategy
Internal links are one of the most effective and underused SEO techniques for developer portfolios. Every blog post on my site links to:
- The homepage (reinforces the main domain's authority)
- Related blog posts (distributes page authority and keeps visitors on the site)
- Relevant project or service pages (drives traffic to conversion-oriented pages)
This is not random link stuffing. Each link adds genuine value for the reader. If I mention TypeScript in a blog post, I link to my TypeScript full stack best practices guide. If I discuss career advice, I link to relevant posts about becoming a full stack developer. This cross-linking builds topical authority — Google sees that my site covers topics comprehensively and interconnectedly.
Performance Optimization
Google has been clear that page speed is a ranking factor. Core Web Vitals — Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS) — directly impact search rankings.
Static Export Performance Wins
Because manjodh.org is a fully static site, performance is inherently excellent:
- LCP under 1 second — HTML is pre-built and served from a CDN. No server processing time.
- Zero CLS — Layouts are determined at build time. No content shifts from late-loading JavaScript.
- Minimal JavaScript — Server Components mean most of the page sends zero client-side JS.
Image Optimization
Images are the most common performance bottleneck on portfolio sites. My approach:
- Compressed assets — All images are optimized before being added to the repository
- Proper dimensions — Images specify width and height to prevent layout shifts
- Lazy loading — Below-the-fold images load only when they enter the viewport
- Modern formats — WebP where possible for smaller file sizes
Font Loading Strategy
Custom fonts can cause layout shifts and slow down rendering. I use next/font to load fonts with the display: swap strategy, which shows a system font immediately and swaps to the custom font once it loads. This prevents invisible text during font loading.
const inter = Inter({
subsets: ["latin"],
display: "swap",
});Dark Mode Implementation
Dark mode is not just a nice-to-have — it reduces eye strain and many developers prefer it. My implementation uses the class strategy with Tailwind CSS:
- A theme provider detects the user's system preference
- A toggle button lets users override the system preference
- The preference is persisted in localStorage
- Tailwind's
dark:variants handle all styling
The key technical detail is preventing flash of wrong theme. By inlining a small script in the <head> that reads localStorage before the page renders, the correct theme is applied immediately — no flash of white background for dark mode users.
// Theme is applied before React hydrates
<script dangerouslySetInnerHTML={{
__html: `
try {
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.classList.toggle('dark', theme === 'dark');
} catch(e) {}
`
}} />Animation Architecture
Animations on a portfolio site should enhance, not distract. I built a BlurFade component using Framer Motion that creates a subtle blur-to-clear entrance animation:
const BlurFade = ({ children, delay = 0 }) => (
<motion.div
initial={{ opacity: 0, filter: "blur(10px)", y: 10 }}
animate={{ opacity: 1, filter: "blur(0px)", y: 0 }}
transition={{ duration: 0.4, delay }}
>
{children}
</motion.div>
);The staggered delay pattern (BLUR_FADE_DELAY * index) creates a cascading entrance effect where elements appear sequentially. This guides the visitor's eye through the content naturally.
Critically, these animations only run on initial page load. They do not re-trigger on every scroll or interaction — that would be annoying and hurt perceived performance.
Blog Architecture with MDX
The blog is a core part of the SEO strategy. Each post is written in MDX (Markdown with JSX support) with frontmatter metadata:
---
title: "Post Title"
publishedAt: "2025-03-05"
summary: "One-line summary for meta description"
---
Content goes here with **markdown** support.Content Processing Pipeline
Blog posts go through a processing pipeline:
- gray-matter parses the frontmatter metadata
- remark processes the markdown content
- rehype-pretty-code adds syntax highlighting to code blocks using Shiki
- The processed content is rendered as React components
This pipeline runs at build time, so the rendered HTML is pre-generated. Visitors get instant page loads with beautifully highlighted code blocks — no client-side processing required.
Blog Listing and Sorting
The blog listing page fetches all MDX files from the content directory, sorts them by publication date, and renders them as cards with titles and summaries. Each card links to the full post using Next.js Link for client-side navigation.
Tailwind CSS and the Design System
The entire site uses Tailwind CSS with CSS custom properties for theming. Colors are defined as HSL values in globals.css:
:root {
--background: 0 0% 100%;
--foreground: 222 84% 5%;
--primary: 222 47% 11%;
/* ... */
}
.dark {
--background: 222 84% 5%;
--foreground: 210 40% 98%;
--primary: 210 40% 98%;
/* ... */
}This approach means I can change the entire color scheme by modifying a few CSS variables. The cn() utility function — combining clsx and tailwind-merge — ensures className merging works correctly even with conditional and conflicting classes.
UI components follow the shadcn/ui pattern using Class Variance Authority (CVA) for type-safe variants. This makes components like buttons and badges consistent and maintainable.
Deployment and Hosting
The site is deployed on Vercel with automatic builds triggered by Git pushes. The deployment pipeline is:
- Push to
mainbranch - Vercel detects the push and triggers a build
next buildgenerates the static export- Static files are distributed to Vercel's global CDN
- Site is live with SSL, HTTP/2, and edge caching
Build times are under 30 seconds. Deployments are atomic — the new version replaces the old one instantly with zero downtime. Preview deployments are generated for pull requests, making it easy to review changes before merging.
Content Strategy for Developer SEO
Having a technically excellent site is necessary but not sufficient. The content strategy is what drives organic traffic over time.
Topical Authority
My blog focuses on a cluster of related topics: full stack development, TypeScript, React Native, mobile development, and software engineering careers. By publishing multiple posts within these topic clusters and cross-linking them, I build topical authority — Google recognizes that manjodh.org is a comprehensive resource on these subjects.
If you are thinking about your own content strategy, you might find these posts valuable for understanding how deep technical content builds authority:
- Full Stack Developer Roadmap for 2025 covers the learning path
- TypeScript Full Stack Best Practices demonstrates technical depth
- Building an AI-Powered Transportation Management System shows how case study content establishes credibility
- How I Built a Full SaaS for Brick Manufacturing is another example of project-driven content
Writing for Developers
Developer content has specific requirements:
- Code examples must work. Nothing destroys credibility faster than broken code in a tutorial.
- Be specific, not generic. "Use TypeScript" is generic. "Use discriminated unions to model API response states" is specific and useful.
- Share what you actually did. Developers can smell theoretical advice a mile away. Write from experience.
- Include the mistakes. What went wrong? What would you do differently? This builds trust.
Connecting Your Portfolio to Business Outcomes
A portfolio is not just a vanity project. It can drive real business outcomes:
- Job offers — Recruiters who find my site through Google can immediately see my work and technical depth
- Client inquiries — Through my digital marketing services page and creative services page, potential clients can reach out directly
- Speaking and mentoring opportunities — Published content establishes expertise that leads to invitations
- Networking — Other developers find and connect with me through my blog posts
Every piece of content on the site serves a purpose in this funnel. The blog attracts visitors. The portfolio showcases capability. The service pages convert interest into action.
Lessons Learned
Building and maintaining this portfolio has taught me several things:
Start simple, iterate. The first version of manjodh.org was basic — just a landing page with projects. The blog, SEO optimization, and service pages came later. Do not wait for perfection to launch.
Consistency beats volume. Publishing one quality post per month is more valuable than publishing ten mediocre posts in a burst and then going silent for six months.
Technical SEO is a multiplier. All the content in the world will not rank if your site is slow, missing structured data, or not indexed properly. The technical foundation amplifies everything you build on top of it.
Measure what matters. Google Search Console shows you which queries bring visitors, which pages rank, and where you are losing clicks. Use that data to guide your content decisions.
What I Would Do Differently
If I were starting this portfolio from scratch today:
- Set up analytics and Search Console from day one. I waited too long to start tracking data.
- Plan the content calendar before building. Knowing what I would write about would have influenced the site architecture.
- Invest in Open Graph images earlier. Custom OG images for each blog post significantly improve click-through rates when shared on social media.
- Add an email capture from the start. Building an audience you own (email list) is more valuable than any social media following.
Get Started with Your Own Portfolio
If this post has inspired you to build your own developer portfolio, here is my practical advice:
- Use Next.js with static export. The developer experience is excellent and the performance is unbeatable for content sites.
- Set up proper SEO from the start. Meta tags, sitemap, robots.txt, and JSON-LD. Do not bolt these on later.
- Start writing immediately. Your first posts will not be great. Publish them anyway. You improve by doing.
- Cross-link everything. Every new post should link to at least 2-3 existing posts and pages.
- Deploy on a custom domain.
yourname.comis worth the $12/year investment.
For more technical guides and career advice, check out more articles on my blog. And if you want to see all of this in action, explore my portfolio.
Building a portfolio is not a one-time project. It is an ongoing investment in your career. Make it count.