Matter AI | Code Reviewer Documentation home pagelight logodark logo
  • Contact
  • Github
  • Sign in
  • Sign in
  • Documentation
  • Blog
  • Discord
  • Github
  • Introduction
    • What is Matter AI?
    Getting Started
    • QuickStart
    Product
    • Security Analysis
    • Code Quality
    • Agentic Chat
    • RuleSets
    • Memories
    • Analytics
    • Command List
    • Configurations
    Patterns
    • Languages
      • Supported Languages
      • Python
      • Java
      • JavaScript
      • TypeScript
      • Node.js
      • React
      • Fastify
      • Next.js
      • Terraform
      • C#
      • C++
      • C
      • Go
      • Rust
      • Swift
      • React Native
      • Spring Boot
      • Kotlin
      • Flutter
      • Ruby
      • PHP
      • Scala
      • Perl
      • R
      • Dart
      • Elixir
      • Erlang
      • Haskell
      • Lua
      • Julia
      • Clojure
      • Groovy
      • Fortran
      • COBOL
      • Pascal
      • Assembly
      • Bash
      • PowerShell
      • SQL
      • PL/SQL
      • T-SQL
      • MATLAB
      • Objective-C
      • VBA
      • ABAP
      • Apex
      • Apache Camel
      • Crystal
      • D
      • Delphi
      • Elm
      • F#
      • Hack
      • Lisp
      • OCaml
      • Prolog
      • Racket
      • Scheme
      • Solidity
      • Verilog
      • VHDL
      • Zig
      • MongoDB
      • ClickHouse
      • MySQL
      • GraphQL
      • Redis
      • Cassandra
      • Elasticsearch
    • Security
    • Performance
    Integrations
    • Code Repositories
    • Team Messengers
    • Ticketing
    Enterprise
    • Enterprise Deployment Overview
    • Enterprise Configurations
    • Observability and Fallbacks
    • Create Your Own GitHub App
    • Self-Hosting Options
    • RBAC
    Patterns
    Languages

    Next.js

    Next.js is a React framework that enables server-side rendering, static site generation, and other advanced features with zero configuration. It provides a powerful foundation for building production-ready React applications.

    Next.js, despite its powerful features and optimizations, has several common anti-patterns that can lead to performance issues, maintenance problems, and poor user experience. Here are the most important anti-patterns to avoid when building Next.js applications.

    // Anti-pattern: Using client-side data fetching for static content
    function ProductPage({ productId }) {
      const [product, setProduct] = useState(null);
      const [loading, setLoading] = useState(true);
      
      useEffect(() => {
        async function fetchProduct() {
          const res = await fetch(`/api/products/${productId}`);
          const data = await res.json();
          setProduct(data);
          setLoading(false);
        }
        
        fetchProduct();
      }, [productId]);
      
      if (loading) return <div>Loading...</div>;
      
      return (
        <div>
          <h1>{product.name}</h1>
          <p>{product.description}</p>
        </div>
      );
    }
    
    // Better approach: Use getStaticProps for static content
    export async function getStaticProps({ params }) {
      const res = await fetch(`https://api.example.com/products/${params.id}`);
      const product = await res.json();
      
      return {
        props: {
          product,
        },
        revalidate: 60, // Optional: Regenerate page after 60 seconds
      };
    }
    
    export async function getStaticPaths() {
      const res = await fetch('https://api.example.com/products');
      const products = await res.json();
      
      const paths = products.map((product) => ({
        params: { id: product.id.toString() },
      }));
      
      return { paths, fallback: 'blocking' };
    }
    
    function ProductPage({ product }) {
      return (
        <div>
          <h1>{product.name}</h1>
          <p>{product.description}</p>
        </div>
      );
    }

    Using client-side data fetching for content that could be pre-rendered leads to poor performance and SEO. Choose the appropriate data fetching method: getStaticProps for static content, getServerSideProps for dynamic content, and client-side fetching only for user-specific or frequently changing data.

    // Anti-pattern: Using regular img tags
    function ProductCard({ product }) {
      return (
        <div className="card">
          <img 
            src={product.imageUrl} 
            alt={product.name} 
            width={300} 
            height={200} 
          />
          <h3>{product.name}</h3>
        </div>
      );
    }
    
    // Better approach: Use Next.js Image component
    import Image from 'next/image';
    
    function ProductCard({ product }) {
      return (
        <div className="card">
          <Image 
            src={product.imageUrl} 
            alt={product.name} 
            width={300} 
            height={200} 
            placeholder="blur"
            blurDataURL="data:image/jpeg;base64,..."
            priority={product.featured}
          />
          <h3>{product.name}</h3>
        </div>
      );
    }

    Regular <img> tags don’t benefit from Next.js’s automatic image optimization. Use the next/image component to get automatic image optimization, lazy loading, and proper sizing.

    // Anti-pattern: Using regular anchor tags
    function Navigation() {
      return (
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/products">Products</a>
        </nav>
      );
    }
    
    // Better approach: Use Next.js Link component
    import Link from 'next/link';
    
    function Navigation() {
      return (
        <nav>
          <Link href="/">
            <a>Home</a>
          </Link>
          <Link href="/about">
            <a>About</a>
          </Link>
          <Link href="/products">
            <a>Products</a>
          </Link>
        </nav>
      );
    }
    
    // In Next.js 13+ with the App Router
    import Link from 'next/link';
    
    function Navigation() {
      return (
        <nav>
          <Link href="/">Home</Link>
          <Link href="/about">About</Link>
          <Link href="/products">Products</Link>
        </nav>
      );
    }

    Regular anchor tags cause full page reloads. Use the next/link component for client-side navigation between pages, which is faster and preserves state.

    // Anti-pattern: Using API routes to fetch data for getStaticProps
    // pages/products/[id].js
    export async function getStaticProps({ params }) {
      // Don't do this - it creates an unnecessary network request
      const res = await fetch(`http://localhost:3000/api/products/${params.id}`);
      const product = await res.json();
      
      return {
        props: { product },
      };
    }
    
    // pages/api/products/[id].js
    export default async function handler(req, res) {
      const { id } = req.query;
      const product = await getProductFromDatabase(id);
      res.status(200).json(product);
    }
    
    // Better approach: Import the data fetching logic directly
    // lib/products.js
    export async function getProductById(id) {
      return await getProductFromDatabase(id);
    }
    
    // pages/products/[id].js
    import { getProductById } from '../../lib/products';
    
    export async function getStaticProps({ params }) {
      const product = await getProductById(params.id);
      
      return {
        props: { product },
      };
    }
    
    // pages/api/products/[id].js
    import { getProductById } from '../../../lib/products';
    
    export default async function handler(req, res) {
      const { id } = req.query;
      const product = await getProductById(id);
      res.status(200).json(product);
    }

    Using API routes to fetch data for getStaticProps or getServerSideProps creates unnecessary network requests. Import the data fetching logic directly in both server-side functions and API routes.

    // Anti-pattern: Using getServerSideProps for semi-static content
    export async function getServerSideProps() {
      const res = await fetch('https://api.example.com/products');
      const products = await res.json();
      
      return {
        props: { products },
      };
    }
    
    // Better approach: Use Incremental Static Regeneration
    export async function getStaticProps() {
      const res = await fetch('https://api.example.com/products');
      const products = await res.json();
      
      return {
        props: { products },
        revalidate: 60, // Regenerate page after 60 seconds if requested
      };
    }

    Using getServerSideProps for content that doesn’t change frequently adds unnecessary server load. Use Incremental Static Regeneration (ISR) with revalidate for content that changes occasionally.

    // Anti-pattern: Loading large JavaScript libraries upfront
    import LargeChart from 'heavy-chart-library';
    
    function DashboardPage() {
      return (
        <div>
          <h1>Dashboard</h1>
          <LargeChart data={someData} />
        </div>
      );
    }
    
    // Better approach: Use dynamic imports and suspense
    import dynamic from 'next/dynamic';
    import { Suspense } from 'react';
    
    const LargeChart = dynamic(() => import('heavy-chart-library'), {
      loading: () => <div>Loading chart...</div>,
      ssr: false, // Disable server-side rendering if the library isn't SSR compatible
    });
    
    function DashboardPage() {
      return (
        <div>
          <h1>Dashboard</h1>
          <Suspense fallback={<div>Loading chart...</div>}>
            <LargeChart data={someData} />
          </Suspense>
        </div>
      );
    }

    Loading large JavaScript libraries upfront hurts performance metrics like Largest Contentful Paint (LCP) and Time to Interactive (TTI). Use dynamic imports, code splitting, and prioritize loading critical content first.

    // Anti-pattern: Using plain JavaScript without type checking
    function UserProfile({ user }) {
      return (
        <div>
          <h1>{user.name}</h1>
          <p>{user.email}</p>
          <p>{user.address.street}, {user.address.city}</p>
        </div>
      );
    }
    
    // Better approach: Use TypeScript
    interface Address {
      street: string;
      city: string;
      zipCode: string;
      country: string;
    }
    
    interface User {
      id: string;
      name: string;
      email: string;
      address: Address;
    }
    
    interface UserProfileProps {
      user: User;
    }
    
    function UserProfile({ user }: UserProfileProps) {
      return (
        <div>
          <h1>{user.name}</h1>
          <p>{user.email}</p>
          <p>{user.address.street}, {user.address.city}</p>
        </div>
      );
    }

    Not using TypeScript can lead to runtime errors and makes refactoring more difficult. Use TypeScript to catch errors at compile time and improve code maintainability.

    // Anti-pattern: Duplicating layout code in every page
    function HomePage() {
      return (
        <div>
          <header>
            <nav>{/* Navigation items */}</nav>
          </header>
          <main>
            <h1>Welcome to our site</h1>
            {/* Page content */}
          </main>
          <footer>
            {/* Footer content */}
          </footer>
        </div>
      );
    }
    
    function AboutPage() {
      return (
        <div>
          <header>
            <nav>{/* Navigation items */}</nav>
          </header>
          <main>
            <h1>About Us</h1>
            {/* Page content */}
          </main>
          <footer>
            {/* Footer content */}
          </footer>
        </div>
      );
    }
    
    // Better approach: Use layout components
    function MainLayout({ children }) {
      return (
        <div>
          <header>
            <nav>{/* Navigation items */}</nav>
          </header>
          <main>{children}</main>
          <footer>
            {/* Footer content */}
          </footer>
        </div>
      );
    }
    
    function HomePage() {
      return (
        <MainLayout>
          <h1>Welcome to our site</h1>
          {/* Page content */}
        </MainLayout>
      );
    }
    
    function AboutPage() {
      return (
        <MainLayout>
          <h1>About Us</h1>
          {/* Page content */}
        </MainLayout>
      );
    }
    
    // In Next.js 13+ with App Router
    // app/layout.js
    export default function RootLayout({ children }) {
      return (
        <html lang="en">
          <body>
            <header>
              <nav>{/* Navigation items */}</nav>
            </header>
            <main>{children}</main>
            <footer>
              {/* Footer content */}
            </footer>
          </body>
        </html>
      );
    }

    Duplicating layout code across pages leads to maintenance issues. Use layout components or the App Router’s layout system to share common UI elements across pages.

    // Anti-pattern: Not configuring Next.js properly
    // next.config.js
    module.exports = {
      // No configuration
    };
    
    // Better approach: Configure Next.js properly
    // next.config.js
    module.exports = {
      images: {
        domains: ['images.example.com'], // Allow images from this domain
        formats: ['image/avif', 'image/webp'], // Specify image formats
      },
      i18n: {
        locales: ['en', 'fr', 'es'],
        defaultLocale: 'en',
      },
      async redirects() {
        return [
          {
            source: '/old-blog/:slug',
            destination: '/blog/:slug',
            permanent: true,
          },
        ];
      },
      async headers() {
        return [
          {
            source: '/:path*',
            headers: [
              {
                key: 'X-Content-Type-Options',
                value: 'nosniff',
              },
              {
                key: 'X-Frame-Options',
                value: 'DENY',
              },
            ],
          },
        ];
      },
    };

    Not configuring Next.js properly means missing out on important optimizations and features. Use the Next.js config file to configure image domains, internationalization, redirects, headers, and other important settings.

    // Anti-pattern: No error handling
    function ProductPage({ product }) {
      return (
        <div>
          <Header />
          <ProductDetails product={product} /> {/* If this crashes, the whole page crashes */}
          <RelatedProducts ids={product.relatedIds} />
          <Footer />
        </div>
      );
    }
    
    // Better approach: Use error boundaries
    import { ErrorBoundary } from 'react-error-boundary';
    
    function ErrorFallback({ error, resetErrorBoundary }) {
      return (
        <div role="alert">
          <p>Something went wrong:</p>
          <pre>{error.message}</pre>
          <button onClick={resetErrorBoundary}>Try again</button>
        </div>
      );
    }
    
    function ProductPage({ product }) {
      return (
        <div>
          <Header />
          <ErrorBoundary 
            FallbackComponent={ErrorFallback}
            onReset={() => {
              // Reset the state of your app here
            }}
          >
            <ProductDetails product={product} />
          </ErrorBoundary>
          <RelatedProducts ids={product.relatedIds} />
          <Footer />
        </div>
      );
    }

    Without error boundaries, a runtime error in a component can break the entire page. Use error boundaries to gracefully handle errors and display fallback UIs.

    // Anti-pattern: Using client components for everything in Next.js 13+
    'use client';
    
    import { useState } from 'react';
    
    async function getData() {
      const res = await fetch('https://api.example.com/data');
      return res.json();
    }
    
    export default function Page() {
      const [count, setCount] = useState(0);
      const data = await getData(); // This will error in client components
      
      return (
        <div>
          <h1>Data: {data.title}</h1>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }
    
    // Better approach: Split into server and client components
    // Page.js (Server Component)
    import { Counter } from './Counter';
    
    async function getData() {
      const res = await fetch('https://api.example.com/data');
      return res.json();
    }
    
    export default async function Page() {
      const data = await getData();
      
      return (
        <div>
          <h1>Data: {data.title}</h1>
          <Counter />
        </div>
      );
    }
    
    // Counter.js (Client Component)
    'use client';
    
    import { useState } from 'react';
    
    export function Counter() {
      const [count, setCount] = useState(0);
      
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }

    Using client components for everything in Next.js 13+ misses the benefits of server components. Use server components for data fetching and rendering static content, and client components only for interactive parts of your UI.

    // Anti-pattern: Not implementing proper caching
    // pages/products/[id].js
    export async function getServerSideProps({ params }) {
      const res = await fetch(`https://api.example.com/products/${params.id}`);
      const product = await res.json();
      
      return {
        props: { product },
      };
    }
    
    // Better approach: Implement proper caching
    // pages/products/[id].js
    export async function getStaticProps({ params }) {
      const res = await fetch(`https://api.example.com/products/${params.id}`, {
        headers: {
          'Cache-Control': 'public, s-maxage=10, stale-while-revalidate=59',
        },
      });
      const product = await res.json();
      
      return {
        props: { product },
        revalidate: 60,
      };
    }
    
    // In Next.js 13+ App Router
    // app/products/[id]/page.js
    export const revalidate = 60; // Revalidate at most every 60 seconds
    
    async function getProduct(id) {
      const res = await fetch(`https://api.example.com/products/${id}`, {
        next: { revalidate: 60 },
      });
      return res.json();
    }
    
    export default async function ProductPage({ params }) {
      const product = await getProduct(params.id);
      
      return (
        <div>
          <h1>{product.name}</h1>
          <p>{product.description}</p>
        </div>
      );
    }

    Not implementing proper caching strategies can lead to unnecessary API calls and slower performance. Use appropriate caching headers and Next.js’s built-in caching mechanisms like ISR and the fetch API’s cache options.

    FastifyTerraform
    websitexgithublinkedin
    Powered by Mintlify
    Assistant
    Responses are generated using AI and may contain mistakes.