React - Next.js and Meta-Frameworks

Once you have some mastery of React, I STRONGLY ADVISE you to move on to a meta-framework. Next.js, Astro, Remix... whichever one, it will revolutionize the way you develop!


Why Meta-Frameworks?

React Alone is Limited

React is just a library for creating interfaces. But for a real web application, you need:

  • 🗺️ Routing: managing pages and navigation
  • Server-Side Rendering (SSR): SEO and performance
  • 🏗️ Static Site Generation (SSG): ultra-fast sites
  • 🖼️ Image optimization: modern formats, lazy loading
  • 📦 Bundle optimization: automatic code splitting
  • 🔧 Configuration: Webpack, Babel, TypeScript...

React + Vite = Not Enough for Production

// ❌ With classic React/Vite - you have to do everything manually
function App() {
  return (
    <div>
      {/* How do you do routing? */}
      {/* How do you optimize images? */}
      {/* How do you manage SEO? */}
      {/* How do you do SSR? */}
    </div>
  )
}
// ✅ With Next.js - everything is automatic!
export default function HomePage() {
  return (
    <div>
      <Head>
        <title>My awesome page - Automatic SEO!</title>
      </Head>
      <Image 
        src="/photo.jpg" 
        alt="Automatically optimized photo"
        width={500}
        height={300}
      />
    </div>
  )
}

// Automatic SSG with this function
export async function getStaticProps() {
  const data = await fetch('https://api.example.com/data')
  return { props: { data } }
}

Next.js - The Boss of Meta-Frameworks

Ultra Simple Setup

npx create-next-app@latest my-nextjs-app
cd my-nextjs-app
npm run dev

Boom! Your Next.js app is running on http://localhost:3000

Project Structure

my-nextjs-app/
├── app/                 # 🆕 App Router (Next.js 13+)
│   ├── layout.tsx       # Global layout
│   ├── page.tsx         # Homepage
│   ├── about/
│   │   └── page.tsx     # Route /about
│   └── blog/
│       ├── page.tsx     # Route /blog
│       └── [id]/
│           └── page.tsx # Dynamic route /blog/123
├── public/              # Static files
├── components/          # Your components
└── next.config.js       # Next.js configuration

Next.js Superpowers

1. App Router - Modern Routing

// app/page.tsx - Homepage
export default function HomePage() {
  return <h1>Home</h1>
}

// app/about/page.tsx - Page /about
export default function AboutPage() {
  return <h1>About</h1>
}

// app/blog/[id]/page.tsx - Dynamic route
export default function BlogPost({ params }) {
  return <h1>Article {params.id}</h1>
}

// app/layout.tsx - Global layout
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <nav>
          <Link href="/">Home</Link>
          <Link href="/about">About</Link>
        </nav>
        {children}
      </body>
    </html>
  )
}

2. Server Components - Insane Performance

// ✅ Server Component - runs on the server
async function ProductsList() {
  // This request is done server-side!
  const products = await fetch('https://api.example.com/products')
  
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}

// ✅ Client Component - for interactivity
'use client'
import { useState } from 'react'

function ProductCard({ product }) {
  const [liked, setLiked] = useState(false)
  
  return (
    <div>
      <h3>{product.name}</h3>
      <button onClick={() => setLiked(!liked)}>
        {liked ? '❤️' : '🤍'
      </button>
    </div>
  )
}

3. Static Site Generation (SSG)

// app/blog/[id]/page.tsx
export default function BlogPost({ params }) {
  return <h1>Article {params.id}</h1>
}

// Generates all pages statically at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts')
  
  return posts.map(post => ({
    id: post.id.toString()
  }))
}

4. Server Actions - Full-stack Without an API

// actions.ts
'use server'

export async function createPost(formData) {
  const title = formData.get('title')
  const content = formData.get('content')
  
  // Save directly to the database!
  await db.posts.create({ title, content })
  
  redirect('/blog')
}

// components/CreatePost.tsx
import { createPost } from './actions'

export default function CreatePost() {
  return (
    <form action={createPost}>
      <input name="title" placeholder="Title" />
      <textarea name="content" placeholder="Content" />
      <button type="submit">Publish</button>
    </form>
  )
}

5. Automatic Optimizations

import Image from 'next/image'
import Link from 'next/link'

export default function HomePage() {
  return (
    <div>
      {/* Automatically optimized image */}
      <Image
        src="/hero.jpg"
        alt="Hero"
        width={800}
        height={400}
        priority // Loads with priority
      />
      
      {/* Automatic prefetching of links */}
      <Link href="/about">About</Link>
      
      {/* Automatic code splitting */}
      <DynamicComponent />
    </div>
  )
}

Practical Examples

Blog with SSG

// app/blog/page.tsx - List of articles
export default async function BlogPage() {
  const posts = await fetch('https://api.example.com/posts')
  
  return (
    <div>
      <h1>My Blog</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>
            <Link href={`/blog/${post.id}`}>
              {post.title}
            </Link>
          </h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}

// app/blog/[id]/page.tsx - Individual article
export default async function BlogPost({ params }) {
  const post = await fetch(`https://api.example.com/posts/${params.id}`)
  
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  )
}

// Static generation of all articles
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts')
  return posts.map(post => ({ id: post.id.toString() }))
}

E-commerce with Server Actions

// app/products/[id]/page.tsx
import { addToCart } from './actions'

export default async function ProductPage({ params }) {
  const product = await fetch(`/api/products/${params.id}`)
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>Price: {product.price}€</p>
      
      <form action={addToCart}>
        <input type="hidden" name="productId" value={product.id} />
        <input type="number" name="quantity" defaultValue={1} />
        <button type="submit">Add to cart</button>
      </form>
    </div>
  )
}

// actions.ts
'use server'
import { cookies } from 'next/headers'

export async function addToCart(formData) {
  const productId = formData.get('productId')
  const quantity = formData.get('quantity')
  
  // Retrieve the cart from cookies
  const cart = cookies().get('cart')?.value || '[]'
  const cartItems = JSON.parse(cart)
  
  // Add the product
  cartItems.push({ productId, quantity: parseInt(quantity) })
  
  // Save to cookies
  cookies().set('cart', JSON.stringify(cartItems))
  
  redirect('/cart')
}

Advanced Configuration

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Image optimizations
  images: {
    domains: ['example.com', 'cdn.example.com'],
    formats: ['image/webp', 'image/avif'],
  },
  
  // Environment variables
  env: {
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
  
  // Redirects
  async redirects() {
    return [
      {
        source: '/old-page',
        destination: '/new-page',
        permanent: true,
      },
    ]
  },
  
  // Security headers
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
        ],
      },
    ]
  },
}

module.exports = nextConfig

Middleware for Auth

// middleware.ts
import { NextResponse } from 'next/server'

export function middleware(request) {
  // Checks auth for protected pages
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    const token = request.cookies.get('auth-token')
    
    if (!token) {
      return NextResponse.redirect(new URL('/login', request.url))
    }
  }
}

export const config = {
  matcher: '/dashboard/:path*',
}

Alternatives to Next.js

🚀 Astro - The Minimalist

// Perfect for sites with little interactivity
---
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
---

<html>
<head>
  <title>My Astro site</title>
</head>
<body>
  <h1>Blog</h1>
  {posts.map(post => (
    <article>
      <h2>{post.title}</h2>
      <p>{post.excerpt}</p>
    </article>
  ))}
</body>
</html>

🎵 Remix - The Full-Stack

// Remix focuses on Web Standards
export async function loader({ params }) {
  return json(await getPost(params.id))
}

export async function action({ request }) {
  const formData = await request.formData()
  await createPost(formData)
  return redirect('/posts')
}

export default function Post() {
  const post = useLoaderData()
  
  return (
    <div>
      <h1>{post.title}</h1>
      <Form method="post">
        <input name="comment" />
        <button type="submit">Comment</button>
      </Form>
    </div>
  )
}

⚡ Vite + React Router - DIY

// If you want to control everything yourself
import { BrowserRouter, Routes, Route } from 'react-router-dom'

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/blog/:id" element={<BlogPost />} />
      </Routes>
    </BrowserRouter>
  )
}

Deployment

Vercel (Creators of Next.js)

npm i -g vercel
vercel --prod

And there you go! Your app is online with HTTPS, CDN, edge functions...

Alternatives

  • Netlify: Simple and effective
  • Railway: Full-stack with database
  • AWS Amplify: Complete AWS ecosystem
  • Self-hosted: Docker + your server

Resources to Go Further

SEO:

  • Title: React : Next.js et les Meta-Frameworks | Formation Frameworks JS
  • Description: Passez au niveau supérieur avec Next.js et les meta-frameworks. Apprenez le routing, le Server-Side Rendering (SSR), le Static Site Generation (SSG), les Server Actions et les optimisations automatiques.
  • H1: React : Next.js et les Meta-Frameworks
  • Canonical: /course/frameworks-javascript/formation-complete-react/nextjs-meta-frameworks