Using App Router in NextJS the right way to achieve SSR always.
Muhammad Bilal
Full-stack Engineer Proficient in Next.js, React, TypeScript, TailwindCSS, React Native, Node.js,FastAPI, Postgresql and NoSQL databases.
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.
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.
'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
Best Practises to be considered when using AppRouter.
Thanks.
Regards