Next.js SEO for Developers – How to Build Highly Performant Apps with Next

As a developer, you know that building modern web applications requires considering not just the underlying functionality but also the end user experience. Two key elements of a great user experience are application performance and discoverability through search engines.

Fortunately, the popular Next.js framework provides an excellent foundation to achieve both of these goals. Next.js is a React framework that enables key optimizations for performance and SEO out of the box. And with the latest Next.js 13 release, it‘s become even more powerful for building production-ready applications that both users and search engine crawlers will love.

In this in-depth guide, we‘ll walk through the most important features, patterns, and best practices for maximizing your Next.js app‘s SEO and performance. Whether you‘re new to Next.js or an experienced pro, this article will equip you with the knowledge you need to build highly optimized, SEO-friendly applications.

Why Next.js for SEO and Performance

Before diving into specifics, let‘s consider what makes Next.js such a powerful tool for technical SEO and web performance. While React itself is an incredibly productive library for building interactive UIs, it doesn‘t prescribe a set architecture for things like data fetching, routing, and page generation. This flexibility is powerful but can lead to suboptimal decisions when it comes to optimizations.

Next.js provides a more opinionated framework on top of React that bakes in smart defaults and abstractions for optimal performance and SEO. Some of its most important features include:

Server-side rendering (SSR): Next.js allows you to render React components on the server, sending fully formed HTML to the client. This is crucial for SEO, as it ensures that crawlers can parse your page content, and also speeds up the initial page load for end users.

Static site generation (SSG): For use cases where your page content doesn‘t need to be dynamically updated on each request, Next.js allows you to pre-render pages at build time. This delivers maximum performance and enables you to host your app on static hosting services.

Automatic code splitting: Next.js automatically splits your JavaScript bundles, ensuring only the minimum necessary code is loaded on each page. This keeps bundle sizes small and avoids slowing down page loads with unnecessary code.

Smart image optimization: Images are often the largest assets on a web page and can significantly slow down loads. Next.js provides an Image component that automatically optimizes images, including compression, resizing, and lazy loading.

API routes: Next.js allows you to easily create API endpoints as Node.js serverless functions, which can be seamlessly integrated with your front-end code. This enables you to keep your front-end lean while still providing dynamic functionality as needed.

By building on top of these baseline features, you can ensure your Next.js app is architected for optimal performance and SEO from the start. But Next.js 13 introduces some brand new concepts that allow for even more granular optimizations.

Next.js 13 Features for Better SEO and Performance

Next.js 13 introduced several significant new features that further improve performance and SEO. Let‘s walk through the most important ones and consider their implications.

The App Directory and React Server Components

The most fundamental shift in Next.js 13 is the introduction of the app directory and React server components. This new routing and component architecture allows you to combine server-rendered and client-rendered components in the same file, providing more granular control over when and how components are rendered.

From an SEO perspective, this is impactful because it allows you to ensure that the most important content on your pages is always rendered on the server and sent as HTML on initial load. You can then add in interactivity and additional content on the client side as needed, without compromising the initial HTML structure.

To use the new app directory, you simply create a top-level app directory in your project and define your routes as folders within it. Each route can contain a page.js file that defines the UI for that route, as well as a layout.js file that defines a common layout for all pages in that route.

Within your pages and layouts, you can define components as either server components or client components. Server components are rendered on the server and can access server-side resources like databases and files. Client components are rendered on the client and can use state and effects just like traditional React components.

Here‘s a simple example of a page.js file that uses both server and client components:

// app/products/[id]/page.js

import { getProductData } from ‘./utils‘;
import AddToCartButton from ‘./AddToCartButton‘;

// Server component
async function ProductDetails({ params }) {
  const productData = await getProductData(params.id);

  return (
    <div>

      <p>{productData.description}</p>

      {/* Client component */}
      <AddToCartButton productId={params.id} />
    </div>
  )
}

export default ProductDetails;

In this example, the ProductDetails component is a server component that fetches product data from a database and renders the product name and description. The AddToCartButton component is a client component that handles the interactivity of adding an item to the cart.

By splitting up the page in this way, we ensure that the core product information is always included in the server-rendered HTML, while still providing a seamless interactive experience on the client side. This pattern can be extended to any part of your application to optimize SEO and first load performance.

Streaming and Suspense for Data Fetching

Another powerful feature introduced in Next.js 13 is the ability to stream rendered content from the server to the client. This means that instead of waiting for the entire page to be rendered on the server before sending it to the client, Next.js can send HTML as it‘s generated, allowing the client to start rendering the page sooner.

This is made possible through the use of React suspense boundaries. Suspense allows you to "suspend" rendering of a component while it‘s waiting for some asynchronous resource, like data from an API. With Next.js 13, you can use suspense to break up your page into smaller chunks that can be streamed independently.

Here‘s a simple example:

// app/products/page.js

import { Suspense } from ‘react‘;
import { getProducts } from ‘./utils‘;
import ProductList from ‘./ProductList‘;

// Server component
async function Products() {
  const products = await getProducts();

  return (
    <div>


      {/* Suspense boundary */}
      <Suspense fallback={<div>Loading products...</div>}>
        <ProductList products={products} />
      </Suspense>
    </div>
  )
}

export default Products;

In this example, the Products component fetches a list of products and passes it to the ProductList component. By wrapping ProductList in a Suspense boundary, we tell Next.js that it can be streamed separately from the rest of the page.

This means that the client will receive and render the page header and "Loading products…" fallback immediately, and then receive and render the actual product list as soon as it‘s ready. This can significantly improve perceived performance, as users will see content sooner, even if some parts of the page are still loading.

From an SEO perspective, this means that your page‘s core content can be delivered and indexed faster, rather than being blocked by slow data fetches or complex rendering. However, it‘s still important to ensure that any content critical for SEO is included in the initial HTML and not solely loaded on the client side.

Image and Font Optimizations

Next.js has long provided an optimized Image component that handles things like resizing, lazy loading, and format conversion. However, Next.js 13 introduces some improvements to this component as well as a new Font component for optimizing web font loading.

The updated Image component now uses native browser lazy loading by default, removing the need for a custom JavaScript implementation. It also includes better accessibility defaults, requiring an alt prop and warning on invalid property combinations.

The new Font component allows you to easily optimize web font loading for better performance and reduced layout shift. By default, it will automatically inline font CSS, preload fonts, and avoid FOUT (flash of unstyled text) by automatically applying the font-display: optional CSS property.

Here‘s a simple example of using the Font component:

// app/layout.js

import { Inter } from ‘@next/font/google‘;

const inter = Inter({ subsets: [‘latin‘] });

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

In this example, we import the Inter font from Google Fonts and apply it to the root html element. Next.js will automatically optimize the font loading and ensure that it doesn‘t cause any layout shifts.

Using optimized fonts and images is crucial for good web performance, as these are often some of the largest resources on a page. By leveraging Next.js‘ built-in components, you can ensure that your app is serving optimized assets without having to worry about the details of implementation.

Best Practices for Next.js SEO

Now that we‘ve covered some of the key features in Next.js 13 that enable better performance and SEO, let‘s walk through some general best practices to keep in mind as you‘re building your Next.js application.

Use Semantic HTML and Accessible Components

One of the most fundamental aspects of on-page SEO is using semantic, accessible HTML. This means using appropriate heading tags (h1, h2, etc.), landmark elements (header, main, footer, etc.), and ARIA attributes to provide a clear content hierarchy and enable assistive technologies.

Fortunately, Next.js makes this easy by allowing you to write plain HTML within your JSX. Be sure to use semantic tags whenever possible, and consider using a library like @react-aria/ssr to ensure your components are accessible even when rendered on the server.

Optimize Your Page Metadata

Page metadata like title tags, meta descriptions, and Open Graph tags provide important context to search engines about the content of your pages. Next.js 13 provides a new Metadata API for easily adding this metadata to your pages.

Here‘s an example of how you might add metadata to a page:

// app/products/[id]/page.js

export const metadata = {
  title: ‘Product Name | My Ecommerce Site‘,
  description: ‘Product description goes here...‘,
  openGraph: {
    title: ‘Product Name‘,
    description: ‘Product description goes here...‘,
    url: ‘https://myecommercesite.com/products/product-slug‘,
    images: [
      {
        url: ‘https://myecommercesite.com/images/product.jpg‘,
        width: 800,
        height: 600,
      },
    ],
  },
};

export default function ProductPage() {
  // ...
}

In this example, we export a metadata object that contains the page title, description, and Open Graph metadata. Next.js will automatically inject this metadata into the head of the rendered HTML.

Be sure to include unique, relevant metadata for each page in your application. This helps search engines understand the content of your pages and can improve your click-through rates from search results.

Implement Structured Data

Structured data is a standardized format for providing information about a page and classifying the page content. Search engines use structured data to better understand the content of a page, which can lead to richer search results and increased visibility.

Next.js makes it easy to add structured data to your pages using JSON-LD. Here‘s an example of how you might add product structured data:

// app/products/[id]/page.js

import { ProductJsonLd } from ‘next-seo‘;

function ProductDetails({ product }) {
  return (
    <>

      <p>{product.description}</p>

      <ProductJsonLd
        productName={product.name}
        description={product.description}
        brand={product.brand}
        sku={product.sku}
        offers={[
          {
            price: product.price,
            priceCurrency: ‘USD‘,
            availability: ‘https://schema.org/InStock‘,
          },
        ]}
      />
    </>
  )
}

export default ProductDetails;

In this example, we use the ProductJsonLd component from the next-seo library to add product structured data to the page. This includes information like the product name, description, brand, price, and availability.

Adding structured data can enhance your search result snippets and make your pages more attractive to potential visitors. Be sure to include structured data for all your key page templates, such as products, articles, recipes, etc.

Optimize for Core Web Vitals

Core Web Vitals are a set of metrics that measure the real-world user experience of a page. They include metrics like Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS), which measure a page‘s loading performance, interactivity, and visual stability.

Optimizing for Core Web Vitals is important not just for providing a good user experience, but also for SEO. Google has announced that Core Web Vitals are now included as a ranking factor in their search algorithms.

Next.js provides several features out of the box that help optimize for Core Web Vitals, such as automatic code splitting, image optimization, and font optimization. However, there are additional steps you can take to further improve your scores:

  • Ensure your server response times are fast. Use caching, CDNs, and edge rendering where appropriate to reduce TTFB (Time to First Byte).
  • Minimize third-party scripts and lazy load non-critical resources. Each additional script or resource can slow down your page load and negatively impact LCP.
  • Avoid large layout shifts by reserving space for dynamic content. Use CSS aspect ratio boxes or skeleton loading states to prevent content from shifting as it loads.
  • Minimize long tasks on the main thread that can block user input. Break up complex computations into smaller, asynchronous chunks to keep the main thread responsive.

You can use tools like Google Lighthouse and the Chrome DevTools Performance tab to measure your Core Web Vitals scores and identify opportunities for improvement.

Conclusion

Next.js provides a powerful framework for building high-performance, SEO-friendly web applications. By leveraging features like server-side rendering, static generation, and optimized images and fonts, you can create apps that are fast, accessible, and easily discoverable by search engines.

The latest features in Next.js 13, such as the app directory, React server components, and streaming suspense, provide even more tools for optimizing your application‘s performance and SEO. By following best practices like using semantic HTML, optimizing metadata and structured data, and prioritizing Core Web Vitals, you can ensure that your Next.js app is not just functional but also highly optimized for search engines and end users alike.

As you build your Next.js application, keep performance and SEO top of mind from the start. By making informed architectural decisions and leveraging the framework‘s built-in features, you can create an application that not only solves your users‘ needs but also achieves great search visibility and delivers a top-notch experience. Happy coding!

Similar Posts