Forem

Cover image for Optimizing Next.js 15 App Router with template.tsx and CustomProviders
Kamiswara Angga W.
Kamiswara Angga W.

Posted on

Optimizing Next.js 15 App Router with template.tsx and CustomProviders

In modern web development, Server-Side Rendering (SSR) and Search Engine Optimization (SEO) are essential for creating fast, discoverable applications. Next.js has long been celebrated for its ability to handle both efficiently. With Next.js 15 and its new App Router, the framework now enables you to clearly separate server components from client components. One elegant solution to manage this separation while still supporting robust client-side interactivity is to use a dedicated template.tsx file that wraps a centralized CustomProviders component in a Suspense fallback. This approach makes it easy to integrate multiple providers—such as Redux, react-query, and various UI libraries—without compromising SSR or SEO.

In this article, we will cover:

  • An overview of the Next.js 15 App Router and the distinction between server and client components.
  • How to create a centralized CustomProviders component to encapsulate all client-side providers.
  • Wrapping CustomProviders in a Suspense component within template.tsx to manage loading states gracefully.
  • Best practices for integrating client-side providers without affecting SSR and SEO.

Next.js 15 App Router: Merging Server and Client Components

Next.js 15 introduces a new modular approach using the app/ directory, where components are by default treated as server components. This ensures that your pages are rendered on the server, providing fast load times and optimal SEO. However, not every part of your application can rely solely on SSR. Interactive features such as state management, data fetching (using libraries like react-query), and dynamic UI elements require client-side logic.

To enable client-side features, you mark specific components with the "use client" directive. This tells Next.js to treat those components as client components, enabling the use of browser-specific APIs and interactive functionality.


Centralizing Client-Side Logic with CustomProviders

Rather than scattering individual providers throughout your application, a more maintainable approach is to create a single component called CustomProviders. This component can encapsulate multiple providers (e.g., Redux for state management, react-query for data fetching, and any UI-specific context providers). This centralized approach makes your codebase cleaner and easier to maintain.

Here’s how you might define the CustomProviders component:

// components/CustomProviders.tsx
'use client';

import React from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import { store } from '../store';
import { QueryClient, QueryClientProvider } from 'react-query';
// Import any additional providers, e.g., a theme provider from your UI library
import { ThemeProvider } from 'your-ui-library';

const queryClient = new QueryClient();

interface CustomProvidersProps {
  children: React.ReactNode;
}

export default function CustomProviders({ children }: CustomProvidersProps) {
  return (
    <ReduxProvider store={store}>
      <QueryClientProvider client={queryClient}>
        <ThemeProvider attribute="class">
          {children}
        </ThemeProvider>
      </QueryClientProvider>
    </ReduxProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • ReduxProvider: Integrates Redux for global state management.
  • QueryClientProvider: Provides react-query’s QueryClient instance for managing asynchronous data.
  • ThemeProvider: Demonstrates how to include a UI-related provider (replace with your actual UI library provider as needed).

Wrapping CustomProviders with Suspense in template.tsx

To gracefully handle loading states (for example, if any of the providers have asynchronous initialization logic), it is a good idea to wrap CustomProviders within a Suspense component. This can be done inside your template.tsx file:

// app/template.tsx
import React, { Suspense } from 'react';
import CustomProviders from '../components/CustomProviders';

export default function Template({ children }: { children: React.ReactNode }) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <CustomProviders>
        {children}
      </CustomProviders>
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this setup:

  • "use client" directive: Ensures that template.tsx and its children are treated as client components.
  • <Suspense> component: Wraps CustomProviders, providing a fallback UI (here, a simple "Loading..." div) during lazy loading or asynchronous initialization.
  • Centralized client-side interactivity: This structure centralizes all client-side interactivity within CustomProviders, ensuring that pages that don’t need these features can still be server-rendered efficiently.

Best Practices When Using CustomProviders and template.tsx

  • Isolate Client Providers: Keep only the providers and UI components that require client-side behavior within CustomProviders. This ensures that the server-rendered parts of your application remain unaffected.
  • Modularize Providers: As your application grows, consider breaking down providers into smaller sub-components or modules within CustomProviders for easier maintenance.
  • Monitor Performance: As you integrate more client-side logic, keep an eye on bundle size and overall performance to maintain a smooth user experience.
  • Handle Suspense Gracefully: Choose appropriate fallback content for Suspense to provide clear feedback to users during asynchronous operations.

Conclusion

Using a centralized CustomProviders component inside template.tsx is an elegant and maintainable way to integrate multiple client-only providers—such as Redux, react-query, and UI libraries—into your Next.js 15 application. By wrapping CustomProviders in a Suspense component, you can manage loading states effectively while ensuring that SSR and SEO remain intact. This approach provides a clear separation of concerns, ensuring that interactive client-side features do not compromise the performance and search engine visibility of your application.

Happy coding, and may your Next.js projects be both interactive and SEO-friendly!

Top comments (0)