Learning Next.js: Two Months of Building in Production
Two months ago, I knew nothing about Next.js beyond the fact that it was "React but better." Coming from a Python background with zero web development experience, I decided to learn Next.js by building something real: my developer blog. Instead of following tutorials, I jumped straight into building Nikolas Dev Journey using Next.js 15.3.1 with the new App Router. In this post, I'll share what I've learned about Next.js through practical examples from my own blog, covering everything from the basics to deployment.
What is Next.js and Why I Chose It
Next.js is a React framework that adds powerful features like server-side rendering, automatic code splitting, and file-based routing. When I was deciding how to build my blog, I researched several options but Next.js stood out for its combination of simplicity and power. Here's why I chose it over other options:
React-based: Built on React, which is industry-standard and has huge community support
File-based routing: Creating new pages is as simple as adding files to folders
Full-stack capabilities: Can handle both frontend and backend in one framework
Excellent deployment: Works seamlessly with Vercel (made by the same team)
App Router: The new approach makes building complex applications more intuitive
Performance: Built-in optimizations for images, fonts, and code splitting
For a beginner like me, Next.js offered the perfect balance,powerful enough to build a professional blog, but simple enough to learn by doing.
Understanding the App Router: My Project Structure
Next.js 13 introduced the App Router, which completely changed how you structure applications. Instead of a pages directory, everything lives in an app directory. Here's how I structured my blog:
The beauty of this structure is that every page.js file automatically becomes a route. Want a new page? Just create a folder with a page.js file inside. This file-based routing eliminated the complexity of manual route configuration.
Building Layouts: The Root Layout Pattern
One of Next.js's most powerful features is its layout system. The root layout wraps your entire application and is where you define global elements like fonts, metadata, and analytics. Heres my root layout:
Root Layout with Global Configurationjavascript
1import{Inter}from'next/font/google';2import'./globals.css';3importGoogleAnalyticsfrom'./components/GoogleAnalytics';4import{ createMetadata }from'./lib/metadata';56const inter =Inter({7variable:'--font-inter',8subsets:['latin'],9display:'swap',10});1112// Global metadata for the entire site13exportconst metadata =createMetadata('home');1415exportdefaultfunctionRootLayout({ children }){16return(17<html lang="en" className={inter.variable}>18<body
19 style={{20backgroundColor:'#0a0f1c',21color:'#e1e5e9',22margin:0,23minHeight:'100vh',24display:'flex',25flexDirection:'column',26}}27>28<GoogleAnalyticsGA_MEASUREMENT_ID="G-CMEMLMEFFR"/>29{children}3031{/* Structured data for SEO */}32<script
33 type="application/ld+json"34 dangerouslySetInnerHTML={{35__html:JSON.stringify({36"@context":"https://schema.org",37"@type":"Person",38"name":"Nikolas Stojak",39"jobTitle":"Self-taught Software Developer",40"url":"https://nikolasdevjourney.com",41"sameAs":[42"https://www.linkedin.com/in/nikolas-stojak-335177285/",43"https://github.com/Nikostojak"44]45})46}}47/>48</body>49</html>50);51}
This layout runs on every page and handles global concerns like font loading, analytics, and SEO structured data. Next.js automatically optimizes the Google Font loading, which significantly improves performance compared to manual font loading.
Client vs Server Components: When to Use Which
One of the biggest learning curves in Next.js is understanding when to use server components (default) versus client components (marked with 'use client'). Here's what I learned through building my blog:
Server Components (Default)
Server components run on the server and are great for static content and SEO. Most of my blog post pages are server components:
Server Component Example - Blog Postjavascript
1// No 'use client' directive = Server Component2exportconst metadata ={3title:'My Blog Post | Nikolas Dev Journey',4description:'Description of my blog post for SEO',5};67importNavbarfrom'../../../components/Navbar';8importFooterfrom'../../../components/Footer';9importContainerfrom'../../../components/Container';1011exportdefaultfunctionBlogPost(){12return(13<main className="page-layout">14<Navbar/>15<Container>16<article className="blog-post-section">17<h1 className="blog-post-title">18MyBlogPostTitle19</h1>20<div className="blog-post-content">21<p>Static content that gets rendered on the server...</p>22</div>23</article>24</Container>25<Footer/>26</main>27);28}
Client Components for Interactivity
Client components are needed for interactivity, state management, and browser APIs. My navbar with mobile menu is a perfect example:
Client Component Example - Interactive Navbarjavascript
The rule I follow: if it needs state, event handlers, or browser APIs, use 'use client'. If it's just rendering static content, keep it as a server component for better performance.
API Routes: Building a Backend in Next.js
Next.js isn't just a frontend framework,it can handle your backend too. I use API routes for my blog data and contact form. Here's how my blog API works:
API Route Example - Blog Posts Endpointjavascript
1exportasyncfunctionGET(){2try{3const posts =[4{5title:"Learning Next.js: Two Months of Building in Production",6slug:"learning-nextjs-two-months",7date:"2025-06-04",8excerpt:"My practical journey learning Next.js by building a real developer blog.",9category:"Next.js",10tags:["Next.js","React","Web Development"],11isFeatured:true12},13{14title:"From Zero to Functional: My First Month with JavaScript",15slug:"my-javascript-journey",16date:"2025-05-24",17excerpt:"How I learned JavaScript fundamentals by building my blog.",18category:"JavaScript",19tags:["JavaScript","Learning Journey"],20isFeatured:false21},22// More posts...23];2425returnnewResponse(JSON.stringify(posts),{26status:200,27headers:{28"Content-Type":"application/json",29"Cache-Control":"no-store"30}31});32}catch(error){33console.error("Error in /api/blog:", error);34returnnewResponse(35JSON.stringify({error:"Internal Server Error"}),36{status:500}37);38}39}
This API endpoint can be called from anywhere in my app with fetch('/api/blog'). Next.js handles all the server setup,I just focus on writing the logic.
Data Fetching: From Static to Dynamic
Next.js offers multiple ways to fetch data. I started with static data in my components, then moved to API calls for dynamic content. Here's how I fetch blog posts on my homepage:
Client-Side Data Fetching with Error Handlingjavascript
1'use client';2import{ useState, useEffect }from'react';34exportdefaultfunctionHomePage(){5const[posts, setPosts]=useState([]);6const[isLoading, setIsLoading]=useState(true);7const[error, setError]=useState(null);89useEffect(()=>{10asyncfunctionfetchPosts(){11try{12setIsLoading(true);13setError(null);14const res =awaitfetch('/api/blog',{cache:'no-store'});1516if(!res.ok){17thrownewError(`Failed to fetch posts: ${res.status}`);18}1920const data =await res.json();21if(!Array.isArray(data)){22thrownewError('Expected an array of posts');23}24setPosts(data);25}catch(err){26console.error('Error fetching posts:', err);27setError(err.message);28}finally{29setIsLoading(false);30}31}3233fetchPosts();34},[]);3536// Find featured and recent posts37const featuredPost = posts.find(post=> post?.isFeatured);38const recentPosts = posts
39.filter(post=>!post?.isFeatured)40.sort((a, b)=>newDate(b?.date ||0)-newDate(a?.date ||0))41.slice(0,5);4243if(isLoading)return<div>Loading posts...</div>;44if(error)return<div>Error:{error}</div>;4546return(47<main>48{/* Render posts */}49</main>50);51}
Image and Font Optimization
Next.js automatically optimizes images and fonts, which significantly improves performance. Here's how I handle fonts in my project:
Font Optimization with next/fontjavascript
1import{Inter}from'next/font/google';23const inter =Inter({4variable:'--font-inter',5subsets:['latin'],6display:'swap',// Improves loading performance7});89exportdefaultfunctionRootLayout({ children }){10return(11<html lang="en" className={inter.variable}>12<body>13{children}14</body>15</html>16);17}
Next.js downloads the font files, self-hosts them, and eliminates external network requests. This removes layout shift and improves Core Web Vitals automatically.
Metadata and SEO
Next.js makes SEO straightforward with its metadata API. I created a reusable metadata system for my blog:
Reusable Metadata Systemjavascript
1exportconstcreateMetadata=(page, customData ={})=>{2const baseMetadata ={3title:'Nikolas Dev Journey',4description:'Follow my journey from philosophy to software development.',5keywords:'developer, python, javascript, web development',6authors:[{name:'Nikolas Stojak'}],78openGraph:{9type:'website',10locale:'en_US',11url:'https://nikolasdevjourney.com',12siteName:'Nikolas Dev Journey',13images:[{14url:'/og-image.jpg',15width:1200,16height:630,17}]18},1920twitter:{21card:'summary_large_image',22creator:'@NikolasStojak'23},24};2526const pageSpecific ={27home:{28title:'Nikolas Dev Journey | From Philosophy to Code',29},30blog:{31title:'Developer Blog | Nikolas Dev Journey',32},33// More page-specific metadata...34};3536return{37...baseMetadata,38...pageSpecific[page],39...customData
40};41};
Deployment with Vercel
Deploying Next.js apps is incredibly smooth with Vercel. Here's my deployment setup:
My Deployment Processbash
1# 1. Connect GitHub repository to Vercel2# 2. Configure build settings (automatic for Next.js)3# 3. Set environment variables in Vercel dashboard4# 4. Every git push triggers automatic deployment56# Build command (automatic):7npm run build
89# Output directory (automatic):10 .next
1112# Environment variables I set:13RESEND_API_KEY=*******
14GA_MEASUREMENT_ID=G-CMEMLMEFFR
1516# Custom domain setup:17# 1. Add domain in Vercel dashboard18# 2. Update DNS records at domain provider19# 3. Automatic SSL certificate generation
Vercel handles everything automatically,builds, deployments, SSL certificates, and global CDN distribution. The integration is so seamless that I often forget I'm running a production website.
Performance Insights: What I Learned
After two months of building with Next.js, here are the performance insights I've gained:
Automatic code splitting: Next.js only loads the JavaScript needed for each page
Image optimization: Next/Image component automatically optimizes and lazy-loads images
Font optimization: Google Fonts are self-hosted and preloaded
Static generation: Pages are pre-generated at build time when possible
Bundle analysis: Built-in tools help identify large dependencies
Core Web Vitals: Next.js is optimized for Google's performance metrics
Challenges I Faced and How I Solved Them
Learning Next.js wasn't without challenges. Here are the main ones I encountered:
1. Server vs Client Component Confusion
Problem: I initially marked everything as 'use client' because I didn't understand the difference. Solution: I learned to start with server components by default and only add 'use client' when I need interactivity.
2. Hydration Errors
Problem: My components would render differently on server and client, causing hydration mismatches. Solution: I ensured server and client rendered identical HTML, especially for dynamic content.
3. API Route Organization
Problem: I didn't know how to structure API routes properly. Solution: I learned that each API route needs a route.js file with named exports like GET, POST.
What I'd Do Differently
If I started over today, here's what I'd change:
Start with TypeScript: The type safety would have prevented many bugs
Use a CSS framework: Tailwind CSS would have sped up styling significantly
Implement testing early: Jest and Testing Library integration would catch regressions
Set up error boundaries: Better error handling for production
Use a database: Move beyond static API data to a real database like PostgreSQL
Next Steps in My Next.js Journey
After two months with Next.js, I'm excited to explore more advanced features:
Server Actions: Handling form submissions without API routes
Streaming: Progressive page loading with Suspense
Middleware: Request-level logic for authentication and redirects
ISR (Incremental Static Regeneration): Updating static content without rebuilds
Advanced caching: Optimizing data fetching with Next.js cache strategies
Why I Recommend Next.js for Beginners
After building a real production website with Next.js, I'd recommend it to other beginners for these reasons:
Gentle learning curve: You can start simple and add complexity gradually
Excellent documentation: The Next.js docs are clear and comprehensive
Full-stack capabilities: Learn both frontend and backend in one framework
Industry standard: Used by companies like Netflix, TikTok, and Airbnb
Great ecosystem: Huge community and third-party library support
Deployment simplicity: Vercel makes going to production effortless
"Next.js taught me that you don't need to master every concept before building something real. Start with the basics, build your project, and learn advanced features as you need them. Two months later, I have both a production website and solid Next.js skills."
💬 Comments & Discussion
Share your thoughts, ask questions, or discuss this post. Comments are powered by GitHub Discussions.
💬 Comments & Discussion
Share your thoughts, ask questions, or discuss this post. Comments are powered by GitHub Discussions.
💡 Tip: You need a GitHub account to comment. This helps reduce spam and keeps discussions high-quality.