Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Parallel Sequential Fetching
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

Parallel and Sequential Data Fetching

Optimizing data fetching for maximum performance

The way you organize your data fetching can make the difference between a fast page and a slow one. Parallel fetching lets independent requests run simultaneously, while sequential fetching waits for each request to complete before starting the next. Understanding when to use each approach is critical for performance. Fetch data in parallel when possible, sequential when necessary. Let's master both patterns and learn to optimize your data fetching strategy!

The Problem: Request Waterfalls

❌ Sequential Fetching (Slow)

TYPESCRIPT
async function DashboardPage() {
  // Request 1: Wait 1000ms
  const user = await fetch('https://api.example.com/user')
    .then(r => r.json());

  // Request 2: Wait another 800ms (starts after Request 1)
  const posts = await fetch('https://api.example.com/posts')
    .then(r => r.json());

  // Request 3: Wait another 600ms (starts after Request 2)
  const notifications = await fetch('https://api.example.com/notifications')
    .then(r => r.json());

  return <Dashboard user={user} posts={posts} notifications={notifications} />;
}

// ⏱️ Total time: 1000ms + 800ms + 600ms = 2400ms
// ❌ Requests happen one after another (waterfall)
// ❌ Each request waits for the previous one

Request Timeline (Sequential):

Request 1 (User)
1000ms
Request 2 (Posts)
800ms
Request 3 (Notifications)
600ms
Total: 2400ms

✅ Parallel Fetching (Fast)

TYPESCRIPT
async function DashboardPage() {
  // All requests start simultaneously
  const [user, posts, notifications] = await Promise.all([
    fetch('https://api.example.com/user').then(r => r.json()),
    fetch('https://api.example.com/posts').then(r => r.json()),
    fetch('https://api.example.com/notifications').then(r => r.json()),
  ]);

  return <Dashboard user={user} posts={posts} notifications={notifications} />;
}

// ⏱️ Total time: 1000ms (slowest request)
// ✅ All requests happen simultaneously
// ✅ Total time = time of slowest request

Request Timeline (Parallel):

Request 1 (User)
1000ms (slowest)
Request 2 (Posts)
800ms
Request 3 (Notifications)
600ms
Total: 1000ms (60% faster!)

The Performance Impact

In this example, parallel fetching is 60% faster (1000ms vs 2400ms). For pages with many requests, the difference can be even more dramatic!

Parallel Data Fetching

Use parallel fetching when requests are independent and don't depend on each other.

Pattern 1: Promise.all() with Array

app/dashboard/page.tsx
async function DashboardPage() {
  // Fetch multiple independent data sources in parallel
  const [user, stats, recentOrders, notifications] = await Promise.all([
    fetch('https://api.example.com/user').then(r => r.json()),
    fetch('https://api.example.com/stats').then(r => r.json()),
    fetch('https://api.example.com/orders/recent').then(r => r.json()),
    fetch('https://api.example.com/notifications').then(r => r.json()),
  ]);

  return (
    <div className="dashboard">
      <UserProfile user={user} />
      <StatsGrid stats={stats} />
      <RecentOrders orders={recentOrders} />
      <NotificationList notifications={notifications} />
    </div>
  );
}

// ✅ All 4 requests start simultaneously
// ✅ Total time = slowest request
// ✅ Much faster than sequential

Pattern 2: Promise.all() with Named Variables

app/products/page.tsx
async function ProductsPage() {
  // Start all requests simultaneously
  const productsPromise = fetch('https://api.example.com/products')
    .then(r => r.json());
  
  const categoriesPromise = fetch('https://api.example.com/categories')
    .then(r => r.json());
  
  const featuredPromise = fetch('https://api.example.com/products/featured')
    .then(r => r.json());

  // Wait for all to complete
  const [products, categories, featured] = await Promise.all([
    productsPromise,
    categoriesPromise,
    featuredPromise,
  ]);

  return (
    <div>
      <FeaturedProducts products={featured} />
      <CategoryFilter categories={categories} />
      <ProductGrid products={products} />
    </div>
  );
}

// ✅ More explicit - shows intent clearly
// ✅ Easy to add more requests
// ✅ Same performance as previous pattern

Pattern 3: Parallel Fetch with Reusable Functions

lib/api.ts
// Reusable fetch functions
export async function getUser(id: string) {
  const res = await fetch(`https://api.example.com/users/${id}`);
  if (!res.ok) throw new Error('Failed to fetch user');
  return res.json();
}

export async function getUserPosts(userId: string) {
  const res = await fetch(`https://api.example.com/users/${userId}/posts`);
  if (!res.ok) throw new Error('Failed to fetch posts');
  return res.json();
}

export async function getUserFollowers(userId: string) {
  const res = await fetch(`https://api.example.com/users/${userId}/followers`);
  if (!res.ok) throw new Error('Failed to fetch followers');
  return res.json();
}
app/profile/[id]/page.tsx
import { getUser, getUserPosts, getUserFollowers } from '@/lib/api';

async function ProfilePage({ params }: { params: { id: string } }) {
  // Fetch all data in parallel using reusable functions
  const [user, posts, followers] = await Promise.all([
    getUser(params.id),
    getUserPosts(params.id),
    getUserFollowers(params.id),
  ]);

  return (
    <div className="profile">
      <ProfileHeader user={user} followerCount={followers.length} />
      <PostList posts={posts} />
      <FollowerList followers={followers} />
    </div>
  );
}

export default ProfilePage;

// ✅ Clean, reusable functions
// ✅ Type-safe
// ✅ Parallel execution
// ✅ Easy to test

Real-World Example: E-commerce Product Page

app/products/[id]/page.tsx
interface Product {
  id: string;
  title: string;
  price: number;
  description: string;
  category: string;
}

interface Review {
  id: string;
  rating: number;
  comment: string;
  author: string;
}

interface RelatedProduct {
  id: string;
  title: string;
  price: number;
  image: string;
}

async function ProductPage({ params }: { params: { id: string } }) {
  // Fetch 4 independent data sources in parallel
  const [product, reviews, relatedProducts, inventory] = await Promise.all([
    // Product details
    fetch(`https://api.example.com/products/${params.id}`)
      .then(r => r.json()) as Promise<Product>,
    
    // Product reviews
    fetch(`https://api.example.com/products/${params.id}/reviews`)
      .then(r => r.json()) as Promise<Review[]>,
    
    // Related products
    fetch(`https://api.example.com/products/${params.id}/related`)
      .then(r => r.json()) as Promise<RelatedProduct[]>,
    
    // Inventory status
    fetch(`https://api.example.com/products/${params.id}/inventory`)
      .then(r => r.json()),
  ]);

  // Calculate average rating
  const avgRating = reviews.reduce((sum, r) => sum + r.rating, 0) / reviews.length;

  return (
    <div className="container mx-auto px-4 py-8">
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
        {/* Product Info */}
        <div>
          <h1 className="text-4xl font-bold mb-4">{product.title}</h1>
          <div className="flex items-center gap-2 mb-4">
            <span className="text-2xl">⭐</span>
            <span className="text-lg font-semibold">
              {avgRating.toFixed(1)} ({reviews.length} reviews)
            </span>
          </div>
          <p className="text-3xl text-green-600 mb-6">${product.price}</p>
          <p className="text-gray-700 mb-6">{product.description}</p>
          
          {/* Inventory Status */}
          <div className="mb-6">
            {inventory.inStock ? (
              <span className="text-green-600 font-semibold">
                ✓ In Stock ({inventory.quantity} available)
              </span>
            ) : (
              <span className="text-red-600 font-semibold">Out of Stock</span>
            )}
          </div>
          
          <button className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold">
            Add to Cart
          </button>
        </div>

        {/* Product Image */}
        <div>
          <img 
            src={product.image} 
            alt={product.title}
            className="w-full rounded-lg"
          />
        </div>
      </div>

      {/* Reviews Section */}
      <div className="mt-12">
        <h2 className="text-2xl font-bold mb-6">Customer Reviews</h2>
        <div className="space-y-4">
          {reviews.map(review => (
            <div key={review.id} className="border rounded-lg p-4">
              <div className="flex items-center gap-2 mb-2">
                <span className="text-yellow-500">{'⭐'.repeat(review.rating)}</span>
                <span className="font-semibold">{review.author}</span>
              </div>
              <p className="text-gray-700">{review.comment}</p>
            </div>
          ))}
        </div>
      </div>

      {/* Related Products */}
      <div className="mt-12">
        <h2 className="text-2xl font-bold mb-6">You May Also Like</h2>
        <div className="grid grid-cols-2 md:grid-cols-4 gap-6">
          {relatedProducts.map(related => (
            <a 
              key={related.id} 
              href={`/products/${related.id}`}
              className="border rounded-lg p-4 hover:shadow-lg transition"
            >
              <img 
                src={related.image} 
                alt={related.title}
                className="w-full h-48 object-cover rounded mb-3"
              />
              <h3 className="font-semibold mb-2">{related.title}</h3>
              <p className="text-green-600">${related.price}</p>
            </a>
          ))}
        </div>
      </div>
    </div>
  );
}

export default ProductPage;

// ✅ 4 requests in parallel
// ✅ Page loads 70-80% faster than sequential
// ✅ All data available immediately
// ✅ Better user experience

Sequential Data Fetching

Use sequential fetching when one request depends on data from a previous request.

When Sequential Fetching is Necessary

app/user/[id]/page.tsx
async function UserPage({ params }: { params: { id: string } }) {
  // Step 1: Get user data first
  const user = await fetch(`https://api.example.com/users/${params.id}`)
    .then(r => r.json());

  // Step 2: Get user's posts (depends on user data)
  // We need user.postsUrl or specific user info
  const posts = await fetch(
    `https://api.example.com/users/${user.id}/posts?lang=${user.preferredLanguage}`
  ).then(r => r.json());

  // Step 3: Get post statistics (depends on post IDs)
  const postIds = posts.map(p => p.id).join(',');
  const analytics = await fetch(
    `https://api.example.com/analytics?postIds=${postIds}`
  ).then(r => r.json());

  return (
    <div>
      <UserProfile user={user} />
      <PostList posts={posts} analytics={analytics} />
    </div>
  );
}

// ⏱️ Slower, but necessary
// ✅ Each request needs data from the previous one
// ✅ Cannot be parallelized

Example: Nested Dependencies

app/teams/[teamId]/page.tsx
interface Team {
  id: string;
  name: string;
  memberIds: string[];
}

interface Member {
  id: string;
  name: string;
  email: string;
}

interface Project {
  id: string;
  title: string;
  status: string;
}

async function TeamPage({ params }: { params: { teamId: string } }) {
  // Level 1: Get team info
  const team: Team = await fetch(
    `https://api.example.com/teams/${params.teamId}`
  ).then(r => r.json());

  // Level 2: Get team members (needs team.memberIds)
  const members: Member[] = await fetch(
    `https://api.example.com/users?ids=${team.memberIds.join(',')}`
  ).then(r => r.json());

  // Level 3: Get each member's projects (needs member IDs)
  // This CAN be parallelized since all member requests are independent
  const projectsPromises = members.map(member =>
    fetch(`https://api.example.com/users/${member.id}/projects`)
      .then(r => r.json())
  );
  const allProjects: Project[][] = await Promise.all(projectsPromises);

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">{team.name}</h1>
      
      <div className="space-y-8">
        {members.map((member, index) => (
          <div key={member.id} className="border rounded-lg p-6">
            <h2 className="text-xl font-semibold mb-2">{member.name}</h2>
            <p className="text-gray-600 mb-4">{member.email}</p>
            
            <h3 className="font-semibold mb-2">Projects:</h3>
            <div className="space-y-2">
              {allProjects[index].map(project => (
                <div key={project.id} className="flex items-center gap-2">
                  <span className="font-medium">{project.title}</span>
                  <span className={
                    project.status === 'active' 
                      ? 'text-green-600' 
                      : 'text-gray-500'
                  }>
                    • {project.status}
                  </span>
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

export default TeamPage;

// Step 1: Team (sequential - must be first)
// Step 2: Members (sequential - needs team.memberIds)
// Step 3: Projects (parallel - all independent)
// ✅ Optimized: Sequential where needed, parallel where possible

Mixed Approach: Optimizing Both

The best approach often combines parallel and sequential fetching strategically.

Pattern: Parallel Groups in Sequence

app/dashboard/advanced/page.tsx
async function AdvancedDashboard() {
  // Group 1: Initial data (parallel)
  const [user, config] = await Promise.all([
    fetch('https://api.example.com/user').then(r => r.json()),
    fetch('https://api.example.com/config').then(r => r.json()),
  ]);

  // Group 2: User-specific data (parallel, but depends on user)
  const [posts, followers, notifications] = await Promise.all([
    fetch(`https://api.example.com/users/${user.id}/posts`).then(r => r.json()),
    fetch(`https://api.example.com/users/${user.id}/followers`).then(r => r.json()),
    fetch(`https://api.example.com/users/${user.id}/notifications`).then(r => r.json()),
  ]);

  // Group 3: Analytics (parallel, depends on posts)
  const postIds = posts.map(p => p.id);
  const [analytics, engagement] = await Promise.all([
    fetch(`https://api.example.com/analytics?postIds=${postIds.join(',')}`)
      .then(r => r.json()),
    fetch(`https://api.example.com/engagement?postIds=${postIds.join(',')}`)
      .then(r => r.json()),
  ]);

  return (
    <div className="dashboard">
      <UserHeader user={user} />
      <PostGrid posts={posts} analytics={analytics} />
      <EngagementStats engagement={engagement} />
      <FollowerList followers={followers} />
      <NotificationCenter notifications={notifications} />
    </div>
  );
}

// Timeline:
// Group 1: user + config (parallel) - 500ms
// Group 2: posts + followers + notifications (parallel) - 800ms
// Group 3: analytics + engagement (parallel) - 600ms
// Total: 500 + 800 + 600 = 1900ms
//
// If all sequential: 500 + 400 + 600 + 300 + 800 + 600 = 3200ms
// Savings: 1300ms (40% faster!)

Pattern: Optimize Critical Path

app/article/[slug]/page.tsx
async function ArticlePage({ params }: { params: { slug: string } }) {
  // Critical: Get article first (user needs to see content)
  const article = await fetch(
    `https://api.example.com/articles/${params.slug}`
  ).then(r => r.json());

  // Non-critical: Get supplementary data in parallel
  // These enhance the experience but aren't essential
  const [author, relatedArticles, comments] = await Promise.all([
    fetch(`https://api.example.com/users/${article.authorId}`)
      .then(r => r.json()),
    fetch(`https://api.example.com/articles/${params.slug}/related`)
      .then(r => r.json()),
    fetch(`https://api.example.com/articles/${params.slug}/comments`)
      .then(r => r.json()),
  ]);

  return (
    <article className="container mx-auto px-4 py-8">
      {/* Critical content - shows first */}
      <h1 className="text-4xl font-bold mb-4">{article.title}</h1>
      <div 
        className="prose prose-lg mb-8"
        dangerouslySetInnerHTML={{ __html: article.content }}
      />

      {/* Supplementary content */}
      <AuthorBio author={author} />
      <RelatedArticles articles={relatedArticles} />
      <CommentSection comments={comments} />
    </article>
  );
}

// ✅ Article content available quickly
// ✅ Supplementary data loads in parallel
// ✅ Optimized for user experience

Performance Comparison

ScenarioRequestsSequential TimeParallel TimeImprovement
Dashboard (3 requests)32400ms1000ms58% faster
Product page (4 requests)43200ms900ms72% faster
User profile (5 requests)54000ms1200ms70% faster

Performance Impact

Parallel fetching typically provides 50-75% performance improvement for pages with multiple independent requests. This translates to noticeably faster page loads and better user experience.

Fetching Pattern Examples

Examples of parallel, sequential, and mixed fetching patterns

examplesImportant

Select a file or folder to see details

Decision Guide: Parallel vs Sequential

Use Parallel Fetching When:

  • ✅ Requests are independent
  • ✅ No request needs data from another
  • ✅ All data is needed at the same time
  • ✅ You want the fastest possible load time

Examples:

  • Dashboard with user, stats, notifications
  • Product page with product, reviews, related items
  • Profile page with user, posts, followers

Use Sequential Fetching When:

  • ⚠️ One request depends on another's data
  • ⚠️ You need to process data between requests
  • ⚠️ Requests must happen in a specific order
  • ⚠️ Later requests need IDs or URLs from earlier ones

Examples:

  • User → User's posts → Post analytics
  • Team → Team members → Member projects
  • Article → Author info → Author's other articles

Use Mixed Approach When:

  • 🎯 Some requests depend on others, some don't
  • 🎯 You can group independent requests together
  • 🎯 You want to optimize a complex dependency chain

Pattern:

  1. Fetch critical data first (sequential if needed)
  2. Group independent requests and fetch in parallel
  3. Fetch dependent data in next sequential step
  4. Repeat as needed

Best Practices

1. Default to Parallel

TYPESCRIPT
// ✅ GOOD: Parallel by default
const [a, b, c] = await Promise.all([getA(), getB(), getC()]);

// ❌ BAD: Sequential without reason
const a = await getA();
const b = await getB();
const c = await getC();

2. Identify Dependencies

TYPESCRIPT
// ✅ GOOD: Clear dependencies
const user = await getUser(id);        // Must be first
const [posts, followers] = await Promise.all([
  getUserPosts(user.id),               // Depends on user
  getUserFollowers(user.id),           // Depends on user
]);

// Both posts and followers depend on user but not each other
// So fetch them in parallel after getting user

3. Optimize Critical Path

TYPESCRIPT
// ✅ GOOD: Critical content first
const article = await getArticle(slug);  // Critical
const [comments, related] = await Promise.all([
  getComments(slug),                     // Nice to have
  getRelated(slug),                      // Nice to have
]);

// User sees article quickly, supplementary data loads in parallel

4. Handle Errors Independently

TYPESCRIPT
// ✅ GOOD: Each request handles its own errors
const [user, posts, notifications] = await Promise.all([
  getUser(id),
  getUserPosts(id).catch(() => []),              // Return empty array on error
  getNotifications(id).catch(() => []),          // Don't fail entire page
]);

// If posts fail, page still works with user and notifications

5. Use Promise.allSettled for Optional Data

TYPESCRIPT
// When some requests might fail but page should still work
const results = await Promise.allSettled([
  fetch('https://api.example.com/critical'),
  fetch('https://api.example.com/optional1'),
  fetch('https://api.example.com/optional2'),
]);

// Check each result
const critical = results[0].status === 'fulfilled' 
  ? await results[0].value.json() 
  : null;

const optional1 = results[1].status === 'fulfilled'
  ? await results[1].value.json()
  : null;

// Page works even if optional requests fail

Key Takeaways

  • Parallel fetching is faster - total time = slowest request
  • Sequential creates waterfalls - total time = sum of all requests
  • Use Promise.all() - fetch independent requests in parallel
  • Sequential when dependencies exist - one request needs another's data
  • Mix both approaches - optimize complex dependency chains
  • 50-75% performance improvement - parallel is dramatically faster
  • Default to parallel - only go sequential when necessary
  • Handle errors gracefully - don't let one failure break everything

What's Next?

You've mastered parallel and sequential data fetching! Next, we'll explore Data Fetching Patterns and Strategies—advanced patterns including streaming with Suspense, preloading, and more sophisticated optimization techniques.

These advanced patterns will help you build even faster applications by strategically loading data, streaming content to users progressively, and optimizing the user experience further.

⚡ Performance Rule

When in doubt, fetch in parallel. Sequential fetching should be the exception, not the rule. Always ask: "Does this request truly depend on the previous one?" If not, fetch in parallel!

Test Your Understanding

Question 1 of 4

When should you use parallel data fetching?

Master parallel and sequential data fetching in Next.js! Learn when to use Promise.all() for optimal performance.

Previous
Fetching Data in Server Components
Next
Data Fetching Patterns and Strategies

Master Next.js Performance Optimization

Join 2,000+ developers building high-performance Next.js apps. Get the next lesson on advanced data fetching patterns - 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