Creating Static Sites With Next.js And Program Geek Best Practices

Creating Static Sites With Next.js And Program Geek Best Practices

December 18, 2024
Written By Sumeet Shroff
Explore the best practices for creating static sites with Next.js and gain insights from Program Geek's expert tips on Next.js SSG implementation.

Web Design & Creative, Mobile Development, Next.js Themes

Creating Static Sites With Next.js 14 and Program Geek Best Practices

With the introduction of Next.js 14, building static sites has never been more efficient or exciting. This React-based framework offers a robust App Router, React Server Components, and cutting-edge optimization features for modern web development. In this blog, we’ll dive into creating static sites using Next.js 14, incorporating the latest Program Geek best practices, and exploring how these advancements can transform your web development process.


What Are Static Sites, and Why Use Next.js 14?

Static sites are pre-rendered websites where all pages are built at compile time, not dynamically rendered on the server. They are perfect for scenarios where content doesn’t change frequently or requires blazing-fast load times.

Next.js 14 has revolutionized static site generation (SSG) with its App Router architecture and React Server Components, offering unmatched performance and flexibility.


Key Advancements in Next.js 14 for Static Sites

1. App Router Architecture

The App Router introduces a new way to structure and manage routes in your Next.js project. It simplifies dynamic routing, data fetching, and layout composition for static sites.

Key features include:

  • File-Based Routing with nested layouts.
  • Parallel and Intercepting Routes, offering greater flexibility.
  • Enhanced support for static paths and pre-rendering.

2. React Server Components

React Server Components (RSCs) bring the ability to handle server-side rendering without compromising on client-side interactivity. This is particularly useful for static sites, as it minimizes JavaScript payloads while improving page speed.

3. Optimized Static Site Generation

Static pages are generated at build time with support for:

  • Dynamic Data Fetching: Use generateStaticParams to handle dynamic routes efficiently.
  • Incremental Static Regeneration (ISR): Update only the parts of your site that need refreshing.

4. New Turbo Engine

The TurboPack bundler introduced in Next.js 14 dramatically reduces build times, enabling faster deployment for static sites.


How to Build a Static Site With Next.js 14

Step 1: Set Up Your Project

Start by creating a new Next.js project:

npx create-next-app@latest my-static-site
cd my-static-site

When prompted, choose the App Router option.

Step 2: Create Static Pages

The App Router leverages the /app directory for organizing pages. Each folder represents a route, and you can define static pages by using the generateStaticParams function.

Example structure:

/app
  /about
    page.jsx
  /blog
    [slug]
      page.jsx

Step 3: Fetch Data for Static Generation

Use generateStaticParams to predefine paths for dynamic routes:

export async function generateStaticParams() {
  const posts = await fetch("https://api.example.com/posts").then((res) =>
    res.json()
  );
  return posts.map((post) => ({ slug: post.slug }));
}

export default function BlogPost({ params }) {
  const { slug } = params;
  return <div>Post: {slug}</div>;
}

This ensures that pages for all blog posts are pre-rendered at build time.

Step 4: Deploy to Vercel

Vercel, the creators of Next.js, offers seamless deployment with global CDN support. Push your project to GitHub, link it to Vercel, and your static site is live in minutes.


Program Geek Best Practices for Static Sites

1. Structure Your App Router Effectively

Organize routes and layouts to reduce redundancy. The App Router allows nested layouts, making it easier to maintain consistent headers, footers, or sidebars across pages.

Example:

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <header>Header</header>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  );
}

Detailed Explanation


2. Leverage React Server Components (RSCs)

React Server Components (RSCs) are a key feature of Next.js 14, designed to optimize performance by reducing the amount of JavaScript sent to the client. Let’s break this down:

What Are React Server Components?

RSCs allow components to be rendered entirely on the server, without shipping their JavaScript to the client. This differs from traditional React components, where the JavaScript is sent to the browser to enable interactivity.

Why Use RSCs for Static Sites?
  • Minimized JavaScript Payload: RSCs ensure that only the necessary data and markup are sent to the client, significantly reducing the amount of JavaScript required.
  • Improved Load Times: Since the browser doesn’t need to execute as much JavaScript, pages load faster.
  • Enhanced SEO: Server-rendered content is more accessible to search engine crawlers, improving SEO rankings.
  • Reduced Client Workload: By handling most of the rendering on the server, you improve user experience, especially on low-powered devices.
How to Implement RSCs in Next.js 14

RSCs work seamlessly with the App Router introduced in Next.js 13 and enhanced in 14. To create a server component, simply define a file within the /app directory and ensure it doesn’t include client-specific code.

Example of a Server Component:

// app/products/page.jsx
export default async function ProductsPage() {
  const products = await fetch("https://api.example.com/products").then((res) =>
    res.json()
  );

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
}

Key Points:

  • The above component fetches data on the server, so no API calls or rendering logic is sent to the client.
  • The final HTML is pre-rendered and served to the client, ensuring faster load times.
Combining RSCs With Client Components

While RSCs are powerful, not everything can or should be rendered on the server. For example, interactive elements like forms or modals require client-side JavaScript. In such cases, you can mix server and client components.

Example of Mixing RSCs and Client Components:

// app/products/page.jsx
import InteractiveButton from './InteractiveButton';

export default async function ProductsPage() {
  const products = await fetch('https://api.example.com/products').then(res => res.json());

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            {product.name} <InteractiveButton productId={product.id} />
          </li>
        ))}
      </ul>
    </div>
  );
}

// app/products/InteractiveButton.jsx
'use client'; // Marks this as a client component
export default function InteractiveButton({ productId }) {
  const handleClick = () => alert(`Product ID: ${productId}`);
  return <button onClick={handleClick}>View Details</button>;
}

3. Optimize Image Delivery

Images often account for the largest portion of web page data, so optimizing them is critical for performance. Next.js 14 offers an enhanced <Image> component that simplifies image optimization with out-of-the-box support for remote domains and modern image formats.

Benefits of Next.js Image Optimization
  1. Automatic Format Conversion: Converts images to modern formats like WebP for smaller file sizes.
  2. Responsive Images: Automatically generates multiple sizes of an image for different screen resolutions and selects the most appropriate one for the user’s device.
  3. Lazy Loading: Delays loading offscreen images until they are needed, improving page load times.
  4. Caching and Delivery via CDN: Images are cached and served from a global CDN, ensuring fast delivery worldwide.
Configuring Image Domains

If your images are hosted on an external domain, you must allowlist the domain in next.config.js:

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "example.com",
        port: "",
        pathname: "/**",
      },
    ],
  },
};

This ensures Next.js can process and optimize images from the specified domain.

Using the <Image> Component

Next.js provides a dedicated <Image> component to handle image optimization. Unlike the standard <img> tag, it automatically optimizes images for performance.

Example:

import Image from "<a href="/blog/how-to-optimize-images-in-nextjs-with-the-image-component">next/image</a>";

export default function HomePage() {
  return (
    <div>
      <h1>Welcome to My Static Site</h1>
      <Image
        src="https://example.com/image.jpg"
        alt="A descriptive alt text"
        width={500}
        height={300}
        quality={90} // Optional: Set the quality level (1-100)
        priority // Optional: Preload this image for faster display
      />
    </div>
  );
}
Features of the <Image> Component
  1. src: The image URL (can be local or remote).
  2. alt: Always include descriptive alt text for accessibility and SEO.
  3. width and height: Specify the dimensions to avoid layout shifts.
  4. priority: Use for critical images (e.g., hero banners) to preload them.
  5. quality: Adjust the compression quality (default is 75).
Dynamic Image Loading

For images with unknown dimensions (e.g., user-uploaded images), use the fill property to make the image adapt to its container:

<Image
  src="https://example.com/image.jpg"
  alt="Dynamic Image"
  fill // Automatically adjusts to the container size
  sizes="(max-width: 768px) 100vw, 50vw" // Optional: Set responsive breakpoints
/>

Best Practices for Images in Static Sites

  1. Compress Images Before Uploading: Use tools like TinyPNG or ImageOptim to reduce file sizes.
  2. Use WebP Format: WebP offers excellent compression without noticeable quality loss.
  3. Set Proper alt Attributes: Improves accessibility and SEO.
  4. Prioritize Above-the-Fold Images: Use the priority attribute for images visible immediately on page load.

By combining React Server Components for reducing client-side JavaScript with advanced image optimization, you can create static sites that are lightning-fast and highly optimized for both users and search engines.

Detailed Explanation


4. Incremental Static Regeneration (ISR)

Incremental Static Regeneration (ISR) is one of the most powerful features of Next.js. It allows you to create static sites that can be updated dynamically and incrementally without needing to rebuild the entire application. This capability is particularly useful for websites that have frequently changing content, such as blogs, e-commerce platforms, or news sites.


What is ISR?

ISR enables you to regenerate static pages on-demand at runtime. Instead of rebuilding the entire site whenever content changes, ISR updates only the specific pages that require changes. This keeps your site fast and efficient while maintaining the benefits of static generation.


How Does ISR Work?

When you use ISR:

  1. Pages are initially generated at build time.
  2. A revalidate period is set (in seconds), determining how often the page should be revalidated.
  3. If a user requests a page and the revalidate period has passed, the server regenerates the page in the background and serves the updated content to subsequent visitors.

How to Implement ISR

To implement ISR, include the revalidate key in the return object of the getStaticProps function.

Here’s a breakdown of the provided code:

export async function getStaticProps() {
  const data = await fetch("https://api.example.com/data");
  return {
    props: { data }, // Pass fetched data to the page
    revalidate: 60, // Revalidate this page every 60 seconds
  };
}
  1. getStaticProps:

    • Fetches data during the build process.
    • Generates a static HTML page.
  2. revalidate:

    • Specifies the time interval (in seconds) after which the page should be regenerated.
    • In this example, the page will revalidate every 60 seconds.
  3. Background Regeneration:

    • When the revalidate period has expired, the page is regenerated in the background after a user request.
    • Visitors never see an outdated page because the regeneration happens seamlessly.

Example Use Case

Imagine you’re building a product catalog for an e-commerce site. Product information changes occasionally, but you don’t want to rebuild the entire site every time a product is updated. ISR allows you to update individual product pages as needed.

export async function getStaticProps({ params }) {
  const product = await fetch(
    `https://api.example.com/products/${params.id}`
  ).then((res) => res.json());
  return {
    props: { product },
    revalidate: 300, // Revalidate every 5 minutes
  };
}

export default function ProductPage({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p>Price: ${product.price}</p>
    </div>
  );
}

Benefits of ISR:

  • Improved Efficiency: Only updated pages are regenerated, saving build time.
  • Enhanced User Experience: Visitors get near-real-time updates without waiting for a full rebuild.
  • Scalability: Even for large sites, ISR ensures efficient content updates.

5. Preload Critical Assets

Preloading critical assets ensures that essential resources, such as fonts, images, or stylesheets, are loaded immediately, improving page performance and user experience. Preloading assets signals to the browser that these files are high-priority and should be fetched as soon as possible.


What is Preloading?

Preloading is a way to inform the browser about resources that are required early in the page lifecycle. The browser will prioritize these assets during page load, reducing render-blocking delays and improving the time to first paint (TTFP).


How to Preload Assets in Next.js

In Next.js, the <Head> component allows you to inject <link> elements into the HTML <head> tag for preloading resources.

Here’s the example code explained:

import Head from "next/head";

export default function HomePage() {
  return (
    <Head>
      <link rel="preload" href="/static/style.css" as="style" />
    </Head>
  );
}
  1. <Head> Component:

    • The Head component in Next.js is used to manage <head> elements, such as meta tags, stylesheets, and scripts.
  2. Preload Example:

    • href: Specifies the file URL to preload.
    • as: Indicates the type of resource being preloaded (e.g., style, image, script).

Types of Preloadable Assets
  1. Fonts:

    • Fonts are critical for page rendering. Preloading them avoids a Flash of Invisible Text (FOIT).
    <Head>
      <link
        rel="preload"
        href="/<a href="/blog/the-20-worst-fonts-ever-avoid-these-design-mistakes">fonts</a>/custom-font.woff2"
        as="font"
        type="font/woff2"
        crossorigin="anonymous"
      />
    </Head>
    
  2. Stylesheets:

    • Preloading stylesheets ensures that the browser applies styles faster.
    <Head>
      <link rel="preload" href="/static/style.css" as="style" />
    </Head>
    
  3. Images:

    • Preloading above-the-fold images improves perceived performance.
    <Head>
      <link rel="preload" href="/static/banner.jpg" as="image" />
    </Head>
    
  4. JavaScript Files:

    • Preload critical scripts for interactive features.
    <Head>
      <link rel="preload" href="/static/script.js" as="script" />
    </Head>
    

When to Use Preloading
  1. Critical Path Optimization:

    • Preload assets that are required to render the visible portion of the page.
  2. Custom Fonts:

    • Always preload fonts that are not installed on the user’s system.
  3. Hero Images:

    • Preload large, above-the-fold images to ensure they are rendered quickly.

Best Practices for Preloading
  1. Avoid Overuse:

    • Preloading too many resources can overwhelm the browser’s request queue, negatively impacting performance.
  2. Combine With Lazy Loading:

    • Use lazy loading for non-critical assets while preloading only the essentials.
  3. Audit Resource Usage:

    • Use browser developer tools to identify critical assets that need preloading.

Combining ISR and Asset Preloading creates a powerful synergy for high-performance static sites. ISR keeps your content fresh and relevant, while preloading ensures essential resources are immediately available to the user, delivering an unparalleled experience.


Advanced Techniques in Next.js 14 for Static Sites

Next.js 14 introduces powerful tools and techniques that take static site development to the next level. Let’s explore these advanced features and how you can implement them effectively.


1. Integrating Third-Party APIs

Combining static generation with data from third-party APIs allows you to create rich, dynamic content while maintaining the performance benefits of a static site. This is especially useful for applications that need regularly updated content, such as blogs, weather dashboards, or e-commerce sites.


How to Integrate APIs During Static Generation
  1. Fetching Data at Build Time: Use the getStaticProps or generateStaticParams functions to fetch data from APIs during the build process. This generates pre-rendered HTML with the fetched data.

    Example:

    export async function getStaticProps() {
      const response = await fetch("https://api.example.com/data");
      const data = await response.json();
      return {
        props: { data },
      };
    }
    
    export default function HomePage({ data }) {
      return (
        <div>
          <h1>Dynamic Data from API</h1>
          <ul>
            {data.map((item) => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        </div>
      );
    }
    
  2. Caching API Responses: To improve build performance, cache API responses locally or use a service like Redis or Vercel Edge Functions. Caching reduces the number of API calls and speeds up the build process.

    Example with caching:

    import cache from "node-cache";
    
    const myCache = new cache();
    
    export async function getStaticProps() {
      const cachedData = myCache.get("apiData");
      if (cachedData) {
        return { props: { data: cachedData } };
      }
    
      const response = await fetch("https://api.example.com/data");
      const data = await response.json();
      myCache.set("apiData", data, 3600); // Cache for 1 hour
      return { props: { data } };
    }
    
  3. Dynamic API Data: For frequently updated content, use Incremental Static Regeneration (ISR) to fetch updated data and regenerate pages dynamically without rebuilding the entire site.

    Example with ISR:

    export async function getStaticProps() {
      const data = await fetch("https://api.example.com/data").then((res) =>
        res.json()
      );
      return {
        props: { data },
        revalidate: 60, // Regenerate page every 60 seconds
      };
    }
    

2. Analytics With App Router

Tracking user behavior is crucial for optimizing your site’s performance and understanding user engagement. Next.js 14’s App Router simplifies the integration of analytics tools like Google Analytics, Mixpanel, or Vercel Analytics.


Built-In Analytics With Vercel

Vercel provides a built-in analytics solution that tracks page views, interactions, and user behavior in real-time. It’s optimized for Next.js and requires no additional setup beyond deployment to Vercel.

Integrating Google Analytics

To integrate Google Analytics with your Next.js project using the App Router:

  1. Install the Google Analytics Script: Add the Google Analytics script to your <Head> component.

    import Head from "next/head";
    
    export default function Layout({ children }) {
      return (
        <>
          <Head>
            <script
              async
              src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"
            ></script>
            <script
              dangerouslySetInnerHTML={{
                __html: `
                  window.dataLayer = window.dataLayer || [];
                  function gtag(){dataLayer.push(arguments);}
                  gtag('js', new Date());
                  gtag('config', 'GA_TRACKING_ID');
                `,
              }}
            />
          </Head>
          <main>{children}</main>
        </>
      );
    }
    
  2. Track Page Views in App Router: Use a custom useEffect hook in your layout component to track navigation events:

    import { usePathname } from "next/navigation";
    import { useEffect } from "react";
    
    export default function AnalyticsTracker() {
      const pathname = usePathname();
    
      useEffect(() => {
        if (window.gtag) {
          window.gtag("config", "GA_TRACKING_ID", {
            page_path: pathname,
          });
        }
      }, [pathname]);
    
      return null;
    }
    
  3. Advanced Event Tracking: Extend tracking to include user interactions, like clicks or form submissions, using custom events:

    function handleClick() {
      window.gtag("event", "button_click", {
        event_category: "engagement",
        event_label: "Sign-Up Button",
      });
    }
    

3. Dynamic Static Sites

Dynamic static sites offer the best of both worlds: the speed and scalability of static sites with the ability to update content dynamically. This is achieved using ISR and the App Router.


Key Use Cases for Dynamic Static Sites
  1. E-Commerce Product Pages:

    • Update product prices or availability dynamically while keeping the rest of the site static.
  2. News and Blogs:

    • Publish breaking news or trending blogs without rebuilding the entire site.
  3. Event Pages:

    • Display real-time updates for upcoming events or conferences.

How to Build Dynamic Static Sites

To create a dynamic static site with Next.js 14, combine the following features:

  1. Use ISR for Real-Time Updates: Regenerate pages dynamically based on the revalidate interval:

    export async function getStaticProps() {
      const events = await fetch("https://api.example.com/events").then((res) =>
        res.json()
      );
      return {
        props: { events },
        revalidate: 300, // Regenerate every 5 minutes
      };
    }
    
  2. Dynamic Routes With the App Router: Use generateStaticParams to predefine dynamic routes.

    export async function generateStaticParams() {
      const products = await fetch("https://api.example.com/products").then(
        (res) => res.json()
      );
      return products.map((product) => ({ slug: product.slug }));
    }
    
    export default function ProductPage({ params }) {
      const { slug } = params;
      // Fetch additional product data here
      return <div>Product: {slug}</div>;
    }
    
  3. Incremental Updates With User Interaction: Add real-time interactivity for pages that require user updates by combining server components with client-side interactivity:

    "use client";
    
    export default function StockChecker({ productId }) {
      const [stock, setStock] = useState(null);
    
      useEffect(() => {
        fetch(`/api/check-stock/${productId}`)
          .then((res) => res.json())
          .then((data) => setStock(data.stock));
      }, [productId]);
    
      return <p>Stock: {stock}</p>;
    }
    

Final Thoughts

By leveraging these advanced techniques in Next.js 14, you can build static sites that are faster, more dynamic, and incredibly efficient. Whether you’re fetching third-party API data, tracking user behavior, or enabling real-time updates, Next.js provides the tools to elevate your static site development.

At Prateeksha Web Design, we specialize in integrating these advanced features to create high-performance static sites tailored to your business needs. Reach out to us to build a site that combines innovation and reliability!

Why Prateeksha Web Design Is the Best Choice

Prateeksha Web Design excels in crafting high-performance static sites using the latest features of Next.js 14. We ensure your website is:

  • SEO-optimized with modern best practices.
  • Responsive and scalable, ready to handle growing traffic.
  • Customized to your brand, ensuring a unique and professional experience.

Our expertise in Next.js SSG, React Server Components, and modern deployment strategies ensures your static site performs exceptionally well.


Encouraging Small Businesses to Act

Static sites built with Next.js 14 provide unparalleled performance, scalability, and reliability. By partnering with Prateeksha Web Design, small businesses can harness the latest advancements in web technology to establish a strong online presence.

Reach out to us today and transform your website into a fast, reliable, and user-friendly platform!

About Prateeksha Web Design

Prateeksha Web Design specializes in creating static sites using Next.js, ensuring fast loading times and improved performance. Our team follows program geek best practices to optimize code quality and maintainability. We prioritize user experience, responsiveness, and accessibility in every project we undertake. Contact us for professional and efficient static site development services that adhere to industry standards.

Interested in learning more? Contact us today.

Sumeet Shroff
Sumeet Shroff
Sumeet Shroff is a leading authority in creating static sites with Next.js and shares essential Program Geek best practices for optimizing Next.js SSG performance.
Loading...