Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Environment Variables
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Environment Variables and Configuration

Managing secrets and configuration

Environment variables let you configure your application without hard-coding values. Store API keys, database URLs, feature flags, and other configuration safely. Next.js supports .env files with automatic loading, NEXT_PUBLIC_ prefix for browser exposure, and environment-specific files. Master environment variables and build secure, configurable applications!

Environment Variable Files

File Types and Priority

Priority Order (highest to lowest):

  1. .env.local - Local overrides (never commit)
  2. .env.[environment] - Environment-specific (.env.development, .env.production)
  3. .env - Base variables (all environments)

Special Files:

  • .env.example - Template showing required variables (commit this!)
  • .env.test - Test environment variables (when NODE_ENV=test)

Basic .env File

.env
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

# API Keys
API_KEY="your-api-key-here"
STRIPE_SECRET_KEY="sk_test_..."

# App Configuration
APP_NAME="My Next.js App"
APP_URL="http://localhost:3000"

# Feature Flags
ENABLE_ANALYTICS=true

# ✅ All environments use these as defaults
# ✅ Can be overridden by .env.local or .env.production
# ✅ Commit this file to version control

Local Overrides (.env.local)

.env.local
# Local development overrides
DATABASE_URL="postgresql://localhost:5432/mydb_dev"
API_KEY="local-development-key"

# Local secrets (NEVER commit this file!)
STRIPE_SECRET_KEY="sk_test_local_..."
JWT_SECRET="local-jwt-secret-123"

# ✅ Highest priority
# ✅ Overrides .env values
# ✅ NEVER commit to version control
# ✅ Add to .gitignore

Environment-Specific Files

.env.development
# Development environment
DATABASE_URL="postgresql://localhost:5432/mydb_dev"
API_URL="http://localhost:3000/api"
ENABLE_DEBUG=true

# ✅ Only loaded when NODE_ENV=development
# ✅ Overrides .env
# ✅ Overridden by .env.local
.env.production
# Production environment
DATABASE_URL="postgresql://prod-server:5432/mydb_prod"
API_URL="https://api.myapp.com"
ENABLE_DEBUG=false

# ✅ Only loaded when NODE_ENV=production
# ✅ Overrides .env
# ✅ Overridden by .env.local

Template File (.env.example)

.env.example
# Database Configuration
DATABASE_URL="postgresql://user:password@host:5432/database"

# API Keys
API_KEY="your-api-key"
STRIPE_SECRET_KEY="sk_..."

# App Configuration
APP_NAME="My App"
APP_URL="http://localhost:3000"

# Feature Flags
ENABLE_ANALYTICS=true

# ✅ Shows required variables
# ✅ No real values
# ✅ Commit to version control
# ✅ Team members copy to .env.local

Accessing Environment Variables

Server-Side Access

app/api/config/route.ts
export async function GET() {
  // Access environment variables
  const dbUrl = process.env.DATABASE_URL;
  const apiKey = process.env.API_KEY;
  const jwtSecret = process.env.JWT_SECRET;
  
  // All variables available on server
  return Response.json({
    hasDbUrl: !!dbUrl,
    hasApiKey: !!apiKey,
    hasJwtSecret: !!jwtSecret,
    // ⚠️ Never expose actual secrets!
  });
}

// ✅ Available in Route Handlers
// ✅ Available in Server Components
// ✅ Available in Server Actions
// ✅ Available in Middleware

Server Component Access

app/dashboard/page.tsx
export default async function DashboardPage() {
  // Access environment variables
  const apiUrl = process.env.API_URL;
  const enableDebug = process.env.ENABLE_DEBUG === 'true';
  
  // Fetch data using env vars
  const data = await fetch(`${apiUrl}/data`, {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`,
    },
  }).then(res => res.json());
  
  return (
    <div>
      <h1>Dashboard</h1>
      {enableDebug && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  );
}

// ✅ All env vars available in Server Components
// ✅ Secure - not exposed to browser

Server Action Access

app/actions/data.ts
'use server';

export async function createData(formData: FormData) {
  const title = formData.get('title');
  
  // Access environment variables
  const dbUrl = process.env.DATABASE_URL;
  const apiKey = process.env.API_KEY;
  
  // Use in database operations
  const db = await connectToDatabase(dbUrl);
  const result = await db.data.create({ title });
  
  return { success: true, id: result.id };
}

// ✅ Available in Server Actions
// ✅ Secure server-side execution

NEXT_PUBLIC_ Variables

Defining Public Variables

.env
# Server-only (not exposed to browser)
API_KEY="secret-key-123"
DATABASE_URL="postgresql://..."

# Public (exposed to browser)
NEXT_PUBLIC_API_URL="https://api.myapp.com"
NEXT_PUBLIC_ANALYTICS_ID="UA-123456"
NEXT_PUBLIC_ENABLE_FEATURE="true"

# ✅ NEXT_PUBLIC_ prefix makes variables public
# ✅ Available in Client Components
# ✅ Bundled into client JavaScript
# ⚠️ Never use for secrets!

Accessing in Client Components

app/components/Analytics.tsx
'use client';

export function Analytics() {
  // ✅ Available in Client Components
  const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;
  const apiUrl = process.env.NEXT_PUBLIC_API_URL;
  
  // ❌ NOT available (undefined)
  const apiKey = process.env.API_KEY;
  const dbUrl = process.env.DATABASE_URL;
  
  React.useEffect(() => {
    if (analyticsId) {
      // Initialize analytics
      initAnalytics(analyticsId);
    }
  }, [analyticsId]);
  
  return <div>Analytics initialized</div>;
}

// ✅ Only NEXT_PUBLIC_ vars available
// ✅ Safe for browser
// ⚠️ Everyone can see these values

Runtime Access in Browser

app/components/ApiClient.tsx
'use client';

export function ApiClient() {
  async function fetchData() {
    // ✅ Available at runtime
    const apiUrl = process.env.NEXT_PUBLIC_API_URL;
    
    const response = await fetch(`${apiUrl}/data`);
    const data = await response.json();
    
    return data;
  }
  
  return (
    <button onClick={fetchData}>
      Fetch Data
    </button>
  );
}

// ✅ NEXT_PUBLIC_ vars embedded in bundle
// ✅ Available in browser DevTools
// ⚠️ Visible to all users

⚠️ NEVER Use NEXT_PUBLIC_ for Secrets

NEXT_PUBLIC_ variables are embedded in the client JavaScript bundle and visible to anyone. Never use them for:

  • API keys for server-to-server communication
  • Database credentials
  • JWT secrets
  • Payment gateway secrets
  • Any sensitive configuration

Use NEXT_PUBLIC_ only for non-sensitive configuration like API URLs, feature flags, or analytics IDs.

Type-Safe Environment Variables

Environment Validation with Zod

lib/env.ts
import { z } from 'zod';

// Define schema
const envSchema = z.object({
  // Server-only
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(1),
  JWT_SECRET: z.string().min(32),
  STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  
  // Public
  NEXT_PUBLIC_API_URL: z.string().url(),
  NEXT_PUBLIC_ANALYTICS_ID: z.string().optional(),
  NEXT_PUBLIC_ENABLE_FEATURE: z
    .string()
    .transform(val => val === 'true')
    .default('false'),
  
  // App config
  NODE_ENV: z.enum(['development', 'production', 'test']),
});

// Validate and export
export const env = envSchema.parse({
  DATABASE_URL: process.env.DATABASE_URL,
  API_KEY: process.env.API_KEY,
  JWT_SECRET: process.env.JWT_SECRET,
  STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
  NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
  NEXT_PUBLIC_ANALYTICS_ID: process.env.NEXT_PUBLIC_ANALYTICS_ID,
  NEXT_PUBLIC_ENABLE_FEATURE: process.env.NEXT_PUBLIC_ENABLE_FEATURE,
  NODE_ENV: process.env.NODE_ENV,
});

// Type-safe exports
export type Env = z.infer<typeof envSchema>;

// ✅ Validates on startup
// ✅ Type-safe access
// ✅ Catches missing variables early
// ✅ Transforms values (string → boolean)

Using Validated Variables

lib/db.ts
import { env } from './env';

export async function connectToDatabase() {
  // ✅ Type-safe and validated
  const dbUrl = env.DATABASE_URL;
  
  const connection = await createConnection({
    url: dbUrl,
  });
  
  return connection;
}

// ✅ TypeScript knows env.DATABASE_URL is a string
// ✅ Validation failed if variable missing
// ✅ No runtime undefined errors

Alternative: t3-env

lib/env.ts
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
  // Server-side environment variables
  server: {
    DATABASE_URL: z.string().url(),
    API_KEY: z.string().min(1),
    JWT_SECRET: z.string().min(32),
  },
  
  // Client-side environment variables (NEXT_PUBLIC_)
  client: {
    NEXT_PUBLIC_API_URL: z.string().url(),
    NEXT_PUBLIC_ANALYTICS_ID: z.string().optional(),
  },
  
  // Values from process.env
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    API_KEY: process.env.API_KEY,
    JWT_SECRET: process.env.JWT_SECRET,
    NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
    NEXT_PUBLIC_ANALYTICS_ID: process.env.NEXT_PUBLIC_ANALYTICS_ID,
  },
});

// ✅ Validates server/client separately
// ✅ Type-safe
// ✅ Enforces NEXT_PUBLIC_ prefix
// ✅ Popular in T3 Stack

Environment Files Structure

Organization of .env files and configuration

project-rootImportant
.envImportant
.env.localImportant
.env.development
.env.production
.env.exampleImportant
.gitignore
app
lib

Select a file or folder to see details

Common Configuration Patterns

Database Configuration

.env
# Development
DATABASE_URL="postgresql://localhost:5432/myapp_dev"
DATABASE_POOL_SIZE=10

# Production (in .env.production or hosting platform)
# DATABASE_URL="postgresql://prod-server/myapp"
# DATABASE_POOL_SIZE=50
lib/db.ts
import { PrismaClient } from '@prisma/client';

const globalForPrisma = global as unknown as {
  prisma: PrismaClient | undefined;
};

export const db =
  globalForPrisma.prisma ??
  new PrismaClient({
    datasourceUrl: process.env.DATABASE_URL,
    log: process.env.NODE_ENV === 'development' 
      ? ['query', 'error', 'warn'] 
      : ['error'],
  });

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = db;
}

// ✅ Uses DATABASE_URL from env
// ✅ Different logging per environment
// ✅ Singleton pattern

API Configuration

.env
# API Endpoints
API_URL="http://localhost:3000/api"
EXTERNAL_API_URL="https://api.external.com"
EXTERNAL_API_KEY="your-api-key"

# Timeouts (milliseconds)
API_TIMEOUT=5000

# Retry Configuration
API_MAX_RETRIES=3
lib/api-client.ts
const API_URL = process.env.API_URL;
const API_TIMEOUT = parseInt(process.env.API_TIMEOUT || '5000', 10);
const MAX_RETRIES = parseInt(process.env.API_MAX_RETRIES || '3', 10);

export async function fetchWithRetry(
  endpoint: string,
  options?: RequestInit,
  retries = MAX_RETRIES
): Promise<Response> {
  const url = `${API_URL}${endpoint}`;
  
  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), API_TIMEOUT);
    
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
    });
    
    clearTimeout(timeout);
    return response;
  } catch (error) {
    if (retries > 0) {
      await new Promise(resolve => setTimeout(resolve, 1000));
      return fetchWithRetry(endpoint, options, retries - 1);
    }
    throw error;
  }
}

// ✅ Configured via env vars
// ✅ Timeout and retry logic
// ✅ Easy to change per environment

Feature Flags

.env
# Feature Flags
NEXT_PUBLIC_ENABLE_ANALYTICS=true
NEXT_PUBLIC_ENABLE_NEW_UI=false
ENABLE_EXPERIMENTAL_FEATURES=false

# A/B Testing
NEXT_PUBLIC_AB_TEST_VARIANT=control
lib/features.ts
export const features = {
  analytics: process.env.NEXT_PUBLIC_ENABLE_ANALYTICS === 'true',
  newUI: process.env.NEXT_PUBLIC_ENABLE_NEW_UI === 'true',
  experimental: process.env.ENABLE_EXPERIMENTAL_FEATURES === 'true',
  abTestVariant: process.env.NEXT_PUBLIC_AB_TEST_VARIANT || 'control',
} as const;

export function isFeatureEnabled(feature: keyof typeof features): boolean {
  return features[feature] === true;
}

// ✅ Centralized feature flags
// ✅ Type-safe access
// ✅ Easy to toggle
app/components/ConditionalFeature.tsx
'use client';

import { features } from '@/lib/features';

export function ConditionalFeature() {
  if (!features.newUI) {
    return <OldUI />;
  }
  
  return <NewUI />;
}

// ✅ Toggle features with env vars
// ✅ No code changes needed
// ✅ Different per environment

Authentication Configuration

.env
# JWT Configuration
JWT_SECRET="your-super-secret-jwt-key-min-32-chars"
JWT_EXPIRES_IN="7d"

# OAuth
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"

# Callback URLs
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-nextauth-secret"
lib/auth.ts
import { SignJWT } from 'jose';

const JWT_SECRET = new TextEncoder().encode(
  process.env.JWT_SECRET
);
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';

export async function createToken(userId: string) {
  const token = await new SignJWT({ userId })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(JWT_EXPIRES_IN)
    .sign(JWT_SECRET);
  
  return token;
}

// ✅ Configured via env vars
// ✅ Different secrets per environment
// ✅ Configurable expiration

Environment Variables Best Practices

1. Use .env.example as Template

.env.example
# Copy this file to .env.local and fill in your values

# Required
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
JWT_SECRET="your-secret-here-min-32-chars"

# Optional
NEXT_PUBLIC_ANALYTICS_ID="UA-XXXXXXX"
ENABLE_DEBUG=false

# ✅ Commit .env.example
# ✅ Shows required variables
# ✅ No real values
# ✅ Team members copy to .env.local

2. Add .env.local to .gitignore

.gitignore
# Environment variables
.env.local
.env*.local

# Keep .env and .env.example
# !.env
# !.env.example

# ✅ Never commit secrets
# ✅ Each developer has their own .env.local

3. Validate on Startup

lib/env.ts
import { z } from 'zod';

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  // ... other variables
});

// Validate immediately
const result = envSchema.safeParse(process.env);

if (!result.success) {
  console.error('❌ Invalid environment variables:');
  console.error(result.error.flatten().fieldErrors);
  throw new Error('Invalid environment variables');
}

export const env = result.data;

// ✅ Fail fast if config invalid
// ✅ Clear error messages
// ✅ Prevents runtime errors

4. Never Hard-Code Secrets

TYPESCRIPT
// ❌ BAD: Hard-coded secrets
const API_KEY = 'sk_live_abc123'; // NEVER!
const DB_URL = 'postgresql://user:password@...'; // NEVER!

// ✅ GOOD: Environment variables
const API_KEY = process.env.API_KEY;
const DB_URL = process.env.DATABASE_URL;

// Always use environment variables for secrets

5. Use Different Values Per Environment

BASH
# .env.development
DATABASE_URL="postgresql://localhost:5432/myapp_dev"
API_URL="http://localhost:3000/api"
ENABLE_DEBUG=true

# .env.production
DATABASE_URL="postgresql://prod-server/myapp_prod"
API_URL="https://api.myapp.com"
ENABLE_DEBUG=false

# ✅ Different configs per environment
# ✅ Appropriate settings for each

6. Document Required Variables

README.md
## Environment Variables

Copy `.env.example` to `.env.local` and configure:

### Required
- `DATABASE_URL`: PostgreSQL connection string
- `JWT_SECRET`: Secret for JWT signing (min 32 chars)
- `STRIPE_SECRET_KEY`: Stripe API secret key

### Optional
- `NEXT_PUBLIC_ANALYTICS_ID`: Google Analytics ID
- `ENABLE_DEBUG`: Enable debug logging (default: false)

### Getting API Keys
- Stripe: https://dashboard.stripe.com/apikeys
- Google Analytics: https://analytics.google.com

# ✅ Clear documentation
# ✅ Helps team members
# ✅ Links to get credentials

Deployment Configuration

Vercel Deployment

BASH
# Set environment variables in Vercel dashboard:
# Settings → Environment Variables

# Or use Vercel CLI:
vercel env add DATABASE_URL production
vercel env add JWT_SECRET production
vercel env add NEXT_PUBLIC_API_URL production

# Pull environment variables locally:
vercel env pull .env.local

# ✅ Secure storage in Vercel
# ✅ Environment-specific values
# ✅ Encrypted at rest

Docker Deployment

Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

# Build with environment variables from build args
ARG DATABASE_URL
ARG NEXT_PUBLIC_API_URL
ENV DATABASE_URL=${DATABASE_URL}
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}

RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]
BASH
# Build with environment variables
docker build \
  --build-arg DATABASE_URL="postgresql://..." \
  --build-arg NEXT_PUBLIC_API_URL="https://api.myapp.com" \
  -t myapp .

# Or use docker-compose.yml with .env file
docker-compose up

# ✅ Environment variables at build time
# ✅ Secure secret management

GitHub Actions Secrets

.github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Build and deploy
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
        run: |
          npm ci
          npm run build
          npm run deploy

# ✅ Secrets in GitHub repository settings
# ✅ Not exposed in logs
# ✅ Encrypted storage

Key Takeaways

  • .env files - automatic loading, multiple environments
  • Priority order - .env.local → .env.[environment] → .env
  • Server-only by default - process.env in server contexts
  • NEXT_PUBLIC_ prefix - expose to browser (never for secrets!)
  • Validate with Zod - type-safe, fail fast
  • Never commit .env.local - add to .gitignore
  • Commit .env.example - template for team
  • Different per environment - dev, staging, production

What's Next?

You've mastered environment variables and configuration! Next, we'll explore Streaming and Suspense—using React Suspense for progressive rendering, streaming server-rendered content, implementing loading states with Suspense boundaries, and building performant, responsive UIs. You'll create fluid user experiences!

We'll cover Suspense boundaries, streaming SSR, loading patterns, and progressive enhancement.

🔐 Security Checklist

  • ✅ Never commit .env.local to version control
  • ✅ Never use NEXT_PUBLIC_ for secrets
  • ✅ Validate all required variables on startup
  • ✅ Use strong, unique secrets for each environment
  • ✅ Rotate secrets regularly
  • ✅ Document required variables in README
  • ✅ Use environment-specific values

Test Your Understanding

Question 1 of 4

How do you expose an environment variable to the browser?

Master environment variables in Next.js! Learn .env files, NEXT_PUBLIC variables, and secure configuration.

Previous
Authentication with Middleware
Next
Streaming and Suspense

Master Next.js Configuration

Join 2,000+ developers building secure, configurable Next.js apps. Get the next lesson on streaming and Suspense - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo