Why I Migrated My Personal Website to Next.js APP Router
Preface
As a Full-Stack Developer focusing on building high-performance, reliable, and scalable web applications, I’m always looking to refine my processes. Recently, I migrated my personal website from React JSX to Next.js APP Router with SSR. In this post, I’ll share why I made this decision, the tangible benefits it brought, and how it’s improved my overall development workflow. I’ll also discuss how leveraging TypeScript’s complete type-safety features, especially with strictNullChecks: true
, and fetch caching played a critical role in enhancing efficiency and reliability.
Introduction
When I first built my personal website, React JSX served its purpose well. But as I became more focused on serverless architecture and scalable solutions, I realised there were limitations holding my site back—particularly around performance and SEO. This led me to switch to Next.js APP Router, which offered powerful new features to improve how my website performs and functions.
Combining Next.js APP Router with TypeScript's complete type-safety features, especially with strictNullChecks: true
, has transformed my development process. By enforcing strict null checks, TypeScript helps catch potential errors at compile-time, ensuring that my application remains robust and reliable. This approach has provided measurable improvements in managing consistent types throughout props sharing.
Why I Made The Change
The migration from React JSX to Next.js APP Router was driven by the following key factors:
Enhanced Performance
- Server-Side Rendering (SSR): Pre-rendering pages on the server dramatically reduces initial load times.
- Static Site Generation (SSG): Building HTML during the build process ensures fast load times for static content.
Improved SEO
- Server-Side Rendering: Content rendered on the server improves crawlability, enhancing visibility on search engines.
- Automatic Metadata Generation: Obtaining the metadata server-side allowed to tell crawlers like Google important information
Here's a simplified example of how I did this on the /blog/[id]/page.tsx
file:
export async function generateMetadata({ params }: Props) {
const blog = await getBlog(params.id);
const description = truncateDescription(blog.content);
return {
title: blog.title,
description,
openGraph: {
title: blog.title,
description,
type: 'article',
images: [{ url: '/assets/img/root/logo-white.png', width: 500, height: 500, alt: 'Featured Image' }],
},
};
}
- Sitemap.xml: Nextjs App Router allows for dynamically creating a sitemap based on fetched data.
Here's a TypeScript example of generating a sitemap using Next.js
with strict type-safety features:
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const url = 'https://www.luke-dev.com';
const staticPaths = [
{ url, changeFrequency: 'yearly', priority: 1 },
{ url: `${url}/portfolio`, changeFrequency: 'monthly', priority: 0.8 },
{ url: `${url}/blog`, changeFrequency: 'weekly', priority: 0.5 },
].map(route => ({ ...route, lastModified: new Date() }));
try {
const blogs = await getBlogs();
const blogPaths = blogs.map(blog => ({
url: `${url}/blog/${blog.id}`,
lastModified: new Date(blog.updatedAt || blog.createdAt || new Date()),
changeFrequency: 'weekly',
priority: 0.6,
}));
return [...staticPaths, ...blogPaths];
} catch (error) {
console.error('Failed to generate blog sitemap:', error);
return staticPaths;
}
}
Improved Routing System
- File-Based Routing: The new file-based routing system in Next.js makes dynamic and nested routes more manageable.
- Using a folder structure like
src/app/blog/[id]
instead of outdated routes files provides a cleaner, more predictable architecture.
Reduced JavaScript Bundle Size
- By using Server Components, I reduced the amount of JavaScript sent to the client, significantly enhancing load times and user experience.
Automatic Image Optimisation
- Next.js’s built-in image optimisation improves both performance and UX by delivering optimised images on demand.
I enjoy how they scale the images based on the screen size, and the setup is quite easy:
<Image
style={{ width: '70px', height: 'auto' }}
className="white"
src="/assets/img/root/logo-white-2.png"
alt="Logo"
loading="lazy"
width={70}
height={70}
/>
Improved Caching
- Implementing
cache: 'force-cache'
with fetch requests ensures that the application doesn’t make unnecessary duplicate requests, enhancing performance and reducing latency.
What Made This Approach Better
TypeScript Configuration
Complete Type-Safety: TypeScript's strict
mode, particularly strictNullChecks: true
, enforces strict type-checking rules, which prevent potential runtime errors by catching null or undefined values during compile-time. This leads to more robust and maintainable code.
Improved Readability & Reliability: The use of TypeScript provides better code readability and predictability, ensuring the codebase remains maintainable over time.
Environment Variables
I kept the variables private because the Next.js APP Router allows me to keep these keys server-side due to Server-Side Rendering (SSR). This means sensitive information doesn't need to be exposed as NEXT_PUBLIC_*
environment variables, which ensures better security and separation of concerns.
Conclusion
Migrating my personal website to Next.js APP Router, along with implementing SSR, TypeScript’s complete type-safety features, and efficient caching mechanisms, has significantly improved its performance, SEO, and maintainability. It’s a worthwhile upgrade that has made my development process smoother, more scalable, and overall, more enjoyable.
Would I recommend this migration for others building modern web applications? Absolutely. The improvements in performance and developer experience alone make it a game-changer.
My Technical Skills

AWS

JavaScript

TypeScript

React

Next.js

Cypress

Figma
