Learn the basic concepts of Next.js 14

Alain IglesiasAlain Iglesias
13 min read

Introduction

Next.js has long been a favored framework for developers seeking to build fast and efficient web applications. With the release of Next.js 14, there's much to be excited about. The evolution of web development tools and frameworks has always been a driving force in the tech industry, guiding how developers create, deploy, and maintain applications.

This article will explore the introduction of new features such as enhanced forms, layouts, caching, authentication methods, accessibility improvements, and middleware enhancements. These advancements are set to redefine development practices by offering more efficient solutions to common challenges, thereby enabling developers to craft more powerful and responsive applications with ease.

New Features and Enhancements

Next.js 14 is packed with features designed to enhance the development experience, improve performance, and offer more flexibility. Here are some of the most notable updates:

Simplifying Data Mutations

Gone are the days of manually creating API Routes. With Next.js 14, developers can define functions that securely run on the server, directly from React components. This approach not only streamlines server-centric data mutations but also integrates with web fundamentals like forms and the FormData Web API. Developers can use Server Actions through a form for progressive enhancement or call them directly as functions, ensuring type-safety with TypeScript and efficient network roundtrips.

export default function Page() {
  async function create(formData) {
    'use server';
    const id = await createItem(formData);
  }
  return (
    <>
      <form onSubmit={create}>
        <button type="submit">Submit</button>
      </form>
    </>
  );
}

Mutations: Updating Data on the Server

Mutations in Next.js 14 are actions that change the state of data on the server. They are essential for operations like creating, updating, or deleting records in a database. Mutations ensure that data integrity is maintained while allowing for responsive user interfaces.

Key Concepts:

  1. Data Integrity: Mutations help maintain consistency in your data by ensuring that changes are applied in a controlled manner.

  2. Optimistic Updates: Next.js supports optimistic updates, where the UI is updated immediately, and the server change is applied in the background. This provides a smoother user experience.

Example: Server Mutation for Database Update

import { mutateServer } from 'next/server';

export default function Dashboard() {
  async function updateUserData(userId, newData) {
    'use server';
    // Update user data in the database
    const updatedUser = await database.users.update(userId, newData);
    return updatedUser;
  }

  return (
    <div>
      {/* UI components */}
      <button onClick={() => updateUserData(1, { name: 'John Doe' })}>Update User</button>
    </div>
  );
}

This example demonstrates a server mutation that updates user data in a database, ensuring that changes are handled securely and efficiently on the server.

Integrating Server Actions with the App Router

The App Router in Next.js 14 provides a streamlined approach to integrating server actions and mutations within your application. This allows for a clear separation of client-side and server-side logic, facilitating better code organization and maintainability.

Steps to Integrate Server Actions:

  1. Define Server Actions: Create server action functions within your component files, marked with 'use server' to indicate server-side execution.

  2. Call Server Actions: Use these functions within your components to handle tasks like data fetching or form submissions.

  3. Handle Responses: Ensure that responses from server actions are properly managed, updating the UI as necessary.

Example: Integrating Server Action in App Router

import { serverAction } from 'next/server';

export default function Profile() {
  async function saveProfile(data) {
    'use server';
    // Save profile data
    const result = await profileService.save(data);
    return result;
  }

  return (
    <div>
      {/* UI components */}
      <form onSubmit={saveProfile}>
        {/* Form fields */}
        <button type="submit">Save Profile</button>
      </form>
    </div>
  );
}

In this setup, the saveProfile function is a server action integrated within the App Router, providing a cohesive and efficient way to handle profile updates.

Practical Tips and Best Practices

  • Use the App Router for a clear distinction between server and client components, ensuring better organization and maintainability.

  • Leverage ISR for pages with content that changes periodically, allowing for automatic updates without the need for manual intervention.

  • Optimize Cache Settings by setting appropriate cache headers and revalidation intervals to balance performance and data freshness.

  • Monitor Data Usage to avoid excessive data fetching, which can lead to performance bottlenecks. Use tools like Next.js analytics to track data fetching metrics.

Integration with Caching and Revalidation

Data Fetching with the App Router

The App Router in Next.js 14 simplifies data fetching by providing a structured way to manage server-side and client-side data needs. The approach emphasizes component-based data fetching, allowing for better separation of concerns and easier maintenance.

Key Concepts:

  1. Server Components: These components handle server-side rendering and can fetch data directly from the server. They do not include client-side JavaScript and are ideal for scenarios where SEO and initial load performance are critical.

  2. Client Components: These components fetch data on the client side and are used for interactive features. They allow for dynamic data fetching using hooks like useEffect and useState.

Example: Fetching Data with Server Components

import { fetchPosts } from '../lib/api';

export default async function Home() {
  const posts = await fetchPosts();
  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

In this example, data is fetched on the server and rendered directly, reducing the need for additional client-side data fetching.

Caching Strategies

Caching is essential for improving performance and reducing server load by storing responses and reusing them for subsequent requests. Next.js 14 offers several caching mechanisms that can be integrated seamlessly with the App Router.

Types of Caching:

  1. Static Generation: Pages are generated at build time and served as static files. This approach is suitable for content that does not change frequently.

  2. Incremental Static Regeneration (ISR): Allows you to update static pages incrementally after the initial build. This ensures that the pages remain up-to-date without rebuilding the entire site.

Implementing ISR Example:

export async function getStaticProps() {
  const posts = await fetchPosts();
  return {
    props: { posts },
    revalidate: 60, // Revalidate every 60 seconds
  };
}

In this setup, the revalidate parameter ensures that the data is refreshed periodically, providing a balance between performance and data freshness.

Revalidation Techniques

Revalidation is the process of updating stale data with fresh data from the server. Next.js 14 makes this process straightforward, allowing developers to set revalidation intervals and manage data consistency effectively.

Revalidation Strategies:

  1. On-Demand Revalidation: Trigger revalidation based on specific events or user actions.

  2. Background Revalidation: Automatically update data in the background while serving stale content until the new data is ready.

Example: On-Demand Revalidation

import { revalidatePath } from 'next/cache';

export default async function handler(req, res) {
  await revalidatePath('/path-to-revalidate');
  res.json({ revalidated: true });
}

This example demonstrates how to trigger revalidation for a specific path, ensuring that the most current data is served to users.

Practical Tips and Best Practices

  • Use the App Router for a clear distinction between server and client components, ensuring better organization and maintainability.

  • Leverage ISR for pages with content that changes periodically, allowing for automatic updates without the need for manual intervention.

  • Optimize Cache Settings by setting appropriate cache headers and revalidation intervals to balance performance and data freshness.

  • Monitor Data Usage to avoid excessive data fetching, which can lead to performance bottlenecks. Use tools like Next.js analytics to track data fetching metrics.

Enhanced Middleware Capabilities

Understanding Middleware

Middleware in Next.js 14 operates at the edge of your application, allowing you to run code before a request is completed. This can be used for a variety of tasks such as modifying response headers, handling authentication, and implementing redirects.

Key Concepts:

  1. Request Interception: Middleware can intercept requests and modify them before they reach the application’s main logic.

  2. Edge Functions: Middleware runs on the edge, closer to the user, which reduces latency and improves performance.

  3. Global Scope: Unlike API routes, middleware can be applied globally across your application, providing a centralized approach to request handling.

Example: Middleware for Authentication

import { NextResponse } from 'next/server';

export function middleware(request) {
  const token = request.cookies.get('auth-token');

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

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

This middleware checks for an authentication token and redirects unauthenticated users to the login page, ensuring that only authorized users can access protected routes.

Middleware Use Cases

Middleware can be used for a wide range of scenarios, from simple request handling to complex logic execution. Here are some common use cases:

  1. Authentication and Authorization: Verify user credentials and permissions before granting access to protected resources.

  2. Localization: Redirect users to different versions of the site based on their location or language preferences.

  3. Analytics and Logging: Collect and log request data for analytics and monitoring purposes.

  4. Content Personalization: Customize content delivery based on user preferences or behaviors.

Faster Code Updates with Fast Refresh

Fast Refresh in Next.js 14 has revolutionized the way we handle code updates which has became up to 96.3% faster. This enhancement allows developers to see changes almost instantaneously, fostering a more dynamic and efficient coding environment. The integration of Fast Refresh with Turbopack means that changes in React components are reflected immediately without the need for a full page reload, making the development cycle quicker and more responsive. Additionally, error handling has been improved, allowing developers to catch and correct mistakes swiftly without disrupting the overall flow.

By streamlining these critical aspects of the development process, Next.js 14 not only enhances productivity but also significantly elevates the user experience for developers.

Improved Image Optimization

Image optimization has always been a strong suit of Next.js, and version 14 takes it a step further. The new image component in Next.js 14 comes with automatic lazy loading and format selection, ensuring that images are delivered in the most optimal format and size. This not only enhances page load speeds but also improves overall user experience.

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

export default function Logo() {
  return (
    <Link href="/" aria-label="Logo">
      <Image priority src="/logomark.png" width={60} height={60} alt="Logomark" />
    </Link>
  )
}

Better TypeScript Support

Next.js 14 brings better integration with TypeScript, making it easier for developers to write and maintain type-safe code. With enhanced TypeScript support, developers can catch errors early in the development process, leading to more robust and reliable applications.

Pro Tip: The new App Router allows end-to-end type safety if you use a content provider or a database that supports Typescript, ex. an ORM.

Improving Accessibility

Accessibility is a crucial aspect of web development, ensuring that all users, including those with disabilities, can effectively interact with your website or application. Next.js 14 emphasizes the importance of building inclusive web experiences by providing tools and best practices for enhancing accessibility. This chapter explores how to integrate accessibility features in your Next.js projects, focusing on strategies, tools, and practical implementations.

The Importance of Accessibility

Web accessibility ensures that websites and applications are usable by everyone, including people with visual, auditory, motor, and cognitive impairments. Accessibility not only promotes inclusivity but also improves the overall user experience and can enhance SEO and legal compliance.

Key Principles of Accessibility:

  1. Perceivable: Information and UI components must be presentable in ways that users can perceive, regardless of their abilities.

  2. Operable: Users must be able to interact with and navigate the interface easily.

  3. Understandable: The content and operation of the interface should be clear and intuitive.

  4. Robust: Content must be robust enough to be interpreted reliably by a wide variety of user agents, including assistive technologies.

Accessibility Features in Next.js 14

Next.js 14 offers several built-in features and recommendations to help developers create accessible applications. By following these guidelines, you can ensure that your applications are inclusive and provide a seamless user experience for everyone.

Key Features:

  1. Semantic HTML: Use HTML elements that convey meaning and structure, aiding assistive technologies in interpreting content.

  2. ARIA Roles and Properties: Utilize Accessible Rich Internet Applications (ARIA) roles and properties to enhance the semantic meaning of UI components.

  3. Focus Management: Ensure that focus is managed appropriately to help users navigate your application using keyboards or other assistive devices.

  4. Responsive Design: Design for a variety of devices and screen sizes, ensuring that content is accessible on mobile and desktop platforms.

Example: Using Semantic HTML

export default function About() {
  return (
    <main>
      <h1>About Us</h1>
      <section>
        <h2>Our Mission</h2>
        <p>We strive to make the web accessible to everyone.</p>
      </section>
    </main>
  );
}

In this example, semantic HTML tags like <main>, <h1>, and <section> provide a clear structure, aiding both users and assistive technologies in understanding the content.

Tools and Techniques for Accessibility

Next.js 14 integrates with various tools and techniques to help you test and improve the accessibility of your applications. These tools can identify issues and provide recommendations for making your site more inclusive.

Key Tools:

  1. Lighthouse: A tool integrated into Chrome DevTools that audits your site for accessibility, performance, and SEO.

  2. Axe: A powerful accessibility testing tool that can be used in browser extensions or as part of your CI/CD pipeline.

  3. Screen Readers: Tools like NVDA and VoiceOver help simulate the experience of visually impaired users, allowing you to test the accessibility of your site.

Example: Running an Accessibility Audit with Lighthouse

  1. Open Chrome DevTools.

  2. Navigate to the "Lighthouse" tab.

  3. Select the "Accessibility" checkbox.

  4. Click "Generate report" to see a detailed audit of your site's accessibility.

The Lighthouse report provides insights and actionable recommendations to enhance your site’s accessibility, such as fixing contrast issues or adding alternative text to images.

Practical Implementation of Accessibility

Incorporating accessibility into your Next.js 14 projects involves following best practices and utilizing the right tools to ensure your site meets accessibility standards.

Best Practices:

  1. Use Alt Text for Images: Provide descriptive alternative text for images to aid users who rely on screen readers.

  2. Ensure Keyboard Navigation: Make sure all interactive elements are accessible via keyboard, and avoid using tabindex values greater than zero.

  3. Provide ARIA Landmarks: Use ARIA roles like role="banner" or role="main" to help users navigate your site more effectively.

  4. Test with Assistive Technologies: Regularly test your application using screen readers and other assistive technologies to identify and address accessibility issues.

Example: Enhancing a Form for Accessibility

export default function ContactForm() {
  return (
    <form aria-labelledby="contact-form-heading">
      <h2 id="contact-form-heading">Contact Us</h2>
      <label htmlFor="name">Name</label>
      <input id="name" name="name" type="text" aria-required="true" />
      <label htmlFor="email">Email</label>
      <input id="email" name="email" type="email" aria-required="true" />
      <button type="submit">Submit</button>
    </form>
  );
}

This form includes labels linked to their corresponding inputs, ARIA labels, and a clear form structure, making it easier for users with assistive technologies to interact with it.

Accessibility Testing and CI/CD Integration

Incorporating accessibility testing into your continuous integration and delivery (CI/CD) process ensures that your application remains accessible throughout development.

Steps to Integrate Accessibility Testing:

  1. Choose a Testing Tool: Select a tool like Axe or Lighthouse CI that fits your development workflow.

  2. Set Up Automated Tests: Configure your CI/CD pipeline to run accessibility tests on every build.

  3. Monitor and Address Issues: Regularly review test results and address any accessibility issues that arise.

Example: Integrating Axe in CI/CD

# Install Axe CLI
npm install -g axe-cli

# Run Axe on your site
axe https://example.com --save --format=json --output=axe-report.json

By integrating Axe into your CI/CD pipeline, you can automatically check for accessibility issues and generate reports, ensuring that accessibility remains a priority throughout development.

Improvements from Previous Versions

To fully appreciate the advancements in Next.js 14, let's compare it with some of the key features from previous versions:

  • Next.js 10 focused on image optimization and internationalization. Next.js 14 takes these features further with enhanced image component capabilities and improved support for global applications.

  • Next.js 12 introduced the innovative middleware API, which laid the groundwork for the advanced capabilities we see in Next.js 14. However, the latest version offers more flexibility and control, making it a significant upgrade.

  • Next.js 13 brought improvements in server-side rendering and static site generation. While these features made development more efficient, Next.js 14 builds on this foundation with faster build times and better caching mechanisms.

Each iteration of Next.js has progressively built on the previous one, and Next.js 14 is no exception. It represents a culmination of the best features from earlier versions, refined and enhanced to meet the evolving needs of developers.

Conclusion

Next.js 14 represents a significant leap forward in web development. With enhanced middleware capabilities, faster build times, improved image optimization, better TypeScript support, and advanced routing features, it offers a powerful toolkit for developers. As we've seen from the real-life examples, companies are already benefiting from these updates, making Next.js 14 a worthy upgrade for any project.

Whether you're building a new application from scratch or looking to improve an existing one, the framework provides the tools you need to create fast, efficient, and high-performing web applications.

0
Subscribe to my newsletter

Read articles from Alain Iglesias directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Alain Iglesias
Alain Iglesias

Software Engineer with more than 10 years of experience in Software Development. He has a knowledge of several programming languages and a Full Stack profile building custom software (with a strong background in Frontend techniques & technologies) for different industries and clients; and lately a deep interest in Data Science, Machine Learning & AI with a solid understanding of Tensorflow, Scikit-learn and Python.