Using App Router in NextJS the right way to achieve SSR always.

Using App Router in NextJS the right way to achieve SSR always.

Hi Dear NextJs developers I would like to share a way that you can use app router to achieve server side rendering while using use client where ever needed in the right way.

The problem faced by devs:

I have also gone to the same problem and I believe that many of the developers are still facing that is having unable to achieve SSR when using app router as app router says to use use client directive where ever useState or useEffect is going to be used. This sometimes confuses new comers because when they use useState or useEffect to fix the use client error they see that the basic purpose for which they were using NextJs is gone and that is why today I will show you how you can achieve SSR 100% while using NextJs App Router.

What we are going to build:

We are going to build a simple NextJs application from scratch using App Router and we will use useState and useEffect hooks so we will work with client side but still we will achieve SSR i.e it means that our page will render html for best SEO. Thats the reason most people transition to NextJS from ReactJS.

  1. So First go to an empty folder and open cmd there and type the following command:-npx create-next-app@latest app_router_demonstrationthen select the below options i.e definately select app router because I am going to demonstrate it

NextJS option

2. Then move to the project directory

cd app_router_demonstration

3. Start the project

npm run dev

Lets write some code

First of all I would like to tell you how you would miss ssr in NextJS and fail on SEO. So this is how you will be failing to achieve SEO.

Suppose I want to create a simple counter and want to use useState

Lets go to the src/app/page.tsx please remove all code and just create a simple button along with a heading which shows counter title. I will use simple useState and will increment it upon button click and on reset it will become zero again and will show counter value simply thats it. So here is how its code will go.

// path: src\app\page.tsx

import { useState } from "react";

export default function Home() {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
  }

  return (
    <main className="flex min-h-screen flex-col items-center justify-evenly">

      <h2 className="text-4xl font-bold text-center">
        Count : {count}
      </h2>

      <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={incrementCount}>
        Counter
      </button>
    </main>
  );
}        

You are going to face error while doing so that

You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

It means now you have to make use of use client

Lets do it and now our code will look like this:

// path: src\app\page.tsx
"use client";
import { useState } from "react";

export default function Home() {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
  }

  return (
    <main className="flex min-h-screen flex-col items-center justify-evenly">

      <h2 className="text-4xl font-bold text-center">
        Count : {count}
      </h2>

      <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={incrementCount}>
        Counter
      </button>
    </main>
  );
}        

Our app is working alright great

But Now lets check the page source.

You can see in the source code that there is our html rendered.

Its that simple however the best practises are to keep the 'use client' only on seperate components.

Problems developers could face and its solution.

  1. Integrating React Redux toolkit Provider the doc says to wrap <Components.../> With Provider which is a solution for pages router however in NextJS App Router you will have to create a seperate component StoreProvider because you cant make layout.tsx client component directly and doing it will give you error because NextJs uses SSR so this is how it will be achieved and make StoreProvider a client side component as react redux runs on client component.StoreProvider.tsx

'use client'
import { useRef } from 'react';
import { Provider } from 'react-redux';
import { makeStore, AppStore } from '../redux/store';

export default function StoreProvider({
    children,
}: {
    children: React.ReactNode
}) {
    const storeRef = useRef<AppStore>()
    if (!storeRef.current) {
        // Create the store instance the first time this renders
        storeRef.current = makeStore()
    }

    return <Provider store={storeRef.current}>{children}</Provider>
}        

And then wraping store provider directly in layout.tsx

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import StoreProvider from "@/providers/StoreProvider";

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

export const metadata: Metadata = {
  title: "App Router Demonstration",
  description: "App Router Usage with React Redux",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <StoreProvider>
           {children}
        </StoreProvider>
      </body>
    </html>
  );
}        

I hope it clears some doubts.

App Router Structure

This is how App Router can be structured in NextJS

App Router Structure.

Best Practises to be considered when using AppRouter.

  1. Always try to keep client components seperate and never use useclient directly in page
  2. Keep common components seperate in components folder
  3. Rendering Navbar and Footer again and again is not a great thing. Just wrap up your entire children at layout.tsx with <MainLayout>{children}</MainLayout> and MainLayout component can have Navbar and Footer while keeping the {children} in between this will make sure that Navbar and Footer arent repeatedly called again and again.
  4. Always try to avoid window is not defined errors if you have those simply resolve them by using if(typeof window !== undefined){ // use window}else{console.log('window is not defined'};

Thanks.

Regards

Muhammad Bilal

要查看或添加评论,请登录

Muhammad Bilal的更多文章

社区洞察

其他会员也浏览了