Layout system in Next JS with Typescript using Page Router - Bangla

Layout system in Next JS with Typescript using Page Router - Bangla

একটা Website এর Layout বলতে আমরা সাধারণত বুঝি যে, ঐ Website এর প্রতিটা পেইজের গঠন কেমন হবে। গঠন বলতে উক্ত Website এর প্রতিটা পেইজেই কমন কিছু part থাকে যেমন, Header Section, Footer Section, Sidebar Section, Content Section, ইত্যাদি ইত্যাদি। এই সকল Section গুলোকে একত্রে নিয়ে তৈরি হয় Layout। এই Layout সাধারণত Page এর উপর নির্ভর করে ভিন্ন হতে পারে। যেমন,

১। সাধারন Web page এ যেমন প্রথমে Header অংশ থাকে, তারপর Content অংশ থাকে এটি ঐ Page কে Represent করে এবং সবার শেষে Footer অংশ থাকে। যেমনটা নিচের মতঃ

২। Dashboard এর ক্ষেত্রে যেমন, Side এ Sidebar থাকে, তার পাশে তারপর Content অংশ থাকে এটি ঐ Page কে Represent করে। যেমনটা নিচের মতঃ

পেইজের উপর নির্ভর করে এই Layout ভিন্ন ভিন্ন হয়ে থাকে।

NextJS এ Page Router ব্যবহার করে এই Layout তৈরি করাটা খুবই সিম্পল। জাস্ট ৩টা সিম্পল স্টেপ ফলো করেই কাজটি করা যায়।

Step 1: একটা Component বানাতে হবে, যেখানে উক্ত Layout এর জন্য Necessary Design করা থাকবে যেমন, header, footer etc. এবং উক্ত Component, Prop আকারে children receive করবে। এবং ঐ children কে নির্দিষ্ট জায়গায় Render করে দিতে হবে। এই children এর মধ্যে থাকবে মূলত ঐ Page টা, যে পেইজে এই Component টা আমরা ইউজ করবো।

Step 2: যদি সকল পেইজের জন্য একটাই Layout হয় তাহলে ত কোন কথাই নেই। জাস্ট _app.tsx এ ঐ Layout Component টা Wrap করে দিতে হবে। আর যদি একাধিক Layout হয় বা এমন হয় যে কিছু কিছু Page এর কোন Layout ইউজ করার দরকার না পরে তাহলে _app.tsx এ ছোট্ট একটা Configuration বা condition Add করে দিতে হবে যাতে করে, যে Page এ যে Layout ইউজ করা হবে সেই Page এর জন্য যেন সেই Layout ইউজ হয় আর যে পেইজে যদি কোন Layout ইউজ না করা হয় সেই পেইজের জন্য যেন কোন Layout ইউজ না হয়ে সিম্পল থাকে।

Step 3: এই স্টেপে কিছুই করা লাগবে না শুধু মাত্র, যদি Multiple layout ইউজ করে থাকলে আমাকে বলে দিতে হবে যে, আমার এই পেইজের জন্য কোন Layout টা ইউজ করতে চাই। যদি সকল পেইজের জন্য একই Layout ইউজ হয়ে থাকে তাহলে এই কাজটাও করা লাগবে না।

তাহলে চলুন একদম শুরু থেকে শুরু করে বিভিন্ন Scenario অনুসারে বিভিন্ন উদাহরণের মাধ্যমে আমরা দেখি কতটা ইজি ভাবে আমরা NextJS এ Page Router ব্যবহার করে কোন পেইজের Layout ডিফাইন করার কাজটা শিখতে পারি।

Scenario ১:

যদি এমন হয় যে, আমার পুরা website এর সকল পেইজে একটাই Layout থাকবে তাহলে সেক্ষেত্রেঃ

Step 1: একটা Layout Wrapper component বানাতে হবে যে Children রিসিভ করবে এবং সেই Children এর সাথে Header Footer যুক্ত করে ঐ Children কেই Return করবে। যেমন,

import React, { ReactNode } from "react";
interface ComponentProps {
  children: ReactNode;
}
const RootLayout = ({ children }: ComponentProps) => {
  return (
    <div>
      <div>Header</div>
      <div>{children}</div>
      <div>Footer</div>
    </div>
  );
};

export default RootLayout;

Step 2: NextJS Application যে ফাইল থেকে রান হয় অর্থাৎ, _app.tsx ফাইল যেটি return করছে সেটি RootLayout দিয়ে Wrap করে দিতে হবে। যেমন,

import "@/styles/globals.css";
import type { AppProps } from "next/app";
import RootLayout from "@/components/layouts/RootLayout";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <RootLayout>
      <Component {...pageProps} />
    </RootLayout>
  );
}

খুবই সিম্পল। এখন আমি যে পেইজেই যাই না কেন, সকল পাইজের জন্যই RootLayout টা পেয়ে যাবে।

Scenario ২:

যদি এমন হয় যে, আমার পুরা website এ অনেক Layout থাকতে পারে, আমি আমার প্রয়োজন অনুসারে যেখানে যেটি দরকার সেখানে সেটি ব্যাবহার করব।

সেক্ষেত্রে,

আমার প্রথম Layout,

src/components/layouts/RootLayout.tsx

import React, { ReactNode } from "react";
interface MyComponentProps {
  children: ReactNode;
}
const RootLayout = ({ children }: MyComponentProps) => {
  return (
    <div>
      <div>Header</div>
      <div>{children}</div>
      <div>Footer</div>
    </div>
  );
};

export default RootLayout;

ধরা যাক এই Layout টি আমি Home Page এ use করবো,

সেক্ষেত্রে,

src/pages/index.tsx

import React from 'react';
import RootLayout from '@/components/layouts/RootLayout';

const HomePage = () => {
  return (
    <div>
      Home page
    </div>
  );
};

export default HomePage;
// নিচের অংশটুকুর মাধ্যমে জানিয়ে দিলাম যে, এই পেইজ কোন Layout use করবে।
HomePage.getLayout = function getLayout(page: React.ReactNode) {
  return (
    <RootLayout>
      {page}
    </RootLayout>
  )
}

এখন,
_app.tsx ফাইল এমন ভাবে configure করতে হবে যেন, কোনো পেইজে যদি কোনো Specific Layout use করে থাকে তাহলে সেটি পাবে, আর use না করে থাকলে যেন, normal ভাবেই কাজ করে।

_app.tsx

import "@/styles/globals.css";
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}
export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout || ((page) => page);
  return getLayout(<Component {...pageProps} />);
}

TypeScript হওয়াতে আমারও প্রথম দেখাতে উপরের কোডটা মাথার উপর দিয়ে গিয়েছিলো। কিন্তু একটু খেয়াল করলে দেখতে পাবেন যে, এখানে তেমন কিছুই করা হয়নি শুধুমাত্র নিচের দুটি লাইনের মাধ্যমেই সকল কাজ হচ্ছে।

const getLayout = Component.getLayout || ((page) => page);
return getLayout(<Component {...pageProps} />);

বাকি সব টাইপের খেলা। এই সেইম কোড আপনি এখানে পেয়ে যাবেন। সো নো টেনশন।

আমি Home page এ HomePage.getLayout Property এর মধ্যে function body টা assign করছিলাম সেটা _app.tsxComponent.getLayout এর মধ্যে সেটি পাওয়া যাবে। এখন আমি যদি অন্য পেইজ যেমন, about page এ আমি যদি কোন Layout use না করি তাহলে about page এর জন্য, _app.tsxComponent.getLayout undefined পাবে।

const getLayout = Component.getLayout || ((page) => page); এই লাইনটার মাধ্যমে চেক করছি যে, Component.getLayout এর মধ্যে যদি function body টা থাকে তাহলে, নিচের লাইনে getLayout() function টা Argument হিসেবে <Component {...pageProps} /> নিয়ে কল হবে, এবং সেটি return করবে।

আর যদি Component.getLayout undefined হয় তাহলে বোঝাই যাচ্ছে যে, এটা normal page তাই ((page) => page) এই লাইনটি Execute হবে যার মানে হলো, যে পেইজ টা পাবে সেটিই return করে দিবে।

হয়ে গেলো কাজ। এখন আমি যেকোন পেইজের জন্য তার Layout কোনটা হবে সেটি ঐ পেইজের মধ্যে Assign করে দিতে পারবো। এবং _app.tsxComponent.getLayout সেটি অটো পেয়ে যাবে। যেমন,

ধরা যাক,

/dashboard রাউটের জন্য আমি চাচ্ছি যে, নিচের Layout টা use হোক।

src/components/layouts/DashboardLayout.tsx

import React, { ReactNode } from "react";
interface MyComponentProps {
  children: ReactNode;
}
const DashboardLayout = ({ children }: MyComponentProps) => {
  return (
    <div>
      <aside>Sidebar</aside>
      <main>{children}</main>
    </div>
  );
};

export default DashboardLayout;

এখন dashboard পেইজে উক্ত Layout টি ইউজ করতে গেলে আগের মতো System এ করতে হবে। শুধু যে Layout টা ইউজ করতে চাই সেটি Wrap করে দিতে হবে।

src/pages/dashboard.tsx

import React from "react";
import DashboardLayout from "@/components/layouts/DashboardLayout";

const Dashboard = () => {
  return <div>Dashboard Page</div>;
};

export default Dashboard;

Dashboard.getLayout = function getLayout(page: React.ReactNode) {
  return <DashboardLayout>{page}</DashboardLayout>;
};

ব্যাস, আমাদের কাজ শেষ।

আশা করছি এতোটুকু অন্তত আপনাদের ক্লিয়ার।

জিনসটা কিন্তু অনেক মজার তাই না? যে পেইজে আমার RootLayout দরকার সেখানে আমি RootLayout Wrap করে দিবো আবার যে পেইজে আমার DashboardLayout দরকার সেখানে আমি DashboardLayout Wrap করে দিব। আর যে পেইজে আমার কোন Layout দরকার হবে না সেখানে আমি কোন Layout ইউজ করবো না। কথা ক্লিয়ার? না ভেজাল আছে?

কিন্তু এখানে একটা সমস্যা হচ্ছে, একটা পেইজ, তার জন্য Layout define করাটা অনেক প্যারা হয়ে যাচ্ছে। যেমন,

dashboard.tsx পেইজে DashboardLayout কে ইউজ করতে আমার নিচের কোডটুকু লেখা লাগছে।

Dashboard.getLayout = function getLayout(page: React.ReactNode) {
  return <DashboardLayout>{page}</DashboardLayout>;
};

আবার HomePage পেইজে RootLayout ইউজ করতে হলে আমাকে নিচের নিচের অংশটুকু লেখা লাগছে।

HomePage.getLayout = function getLayout(page: React.ReactNode) {
  return (
    <RootLayout>
      {page}
    </RootLayout>
  )
}

এখন আমার যদি ৫০ টা Page থাকে তাহলে প্রতিবার আমাকে প্রতিটি পেইজের জন্য ওতখানি লাইন কোড লাগবে যেটা খুবই বিরক্তিকর।

চলুন লাইফটা একটু ইজি করার জন্য একটা Utility Function বানায় ফেলি। যে Function কে Argument হিসেবে একটা Page কে দিয়ে call করে দিবো এবং বাকি কাজটা সেই করে নিবে। যেমনটা নিচের মতোঃ

src/utils/layout.tsx

import React, { ReactElement } from "react";
import Layout from "@/components/Layouts/Customer/Layout";

type PageComponentType = React.FC & {
  getLayout?: (page: ReactElement) => ReactElement;
};

// RootLayout কে ইউজ করার জন্য
export const setRootLayout = (pageComponent: PageComponentType): void => {
  pageComponent.getLayout = function getLayout(page: ReactElement) {
    return <RootLayout>{page}</RootLayout>;
  };
};
// DashboardLayout কে ইউজ করার জন্য
export const setDashboardLayout = (pageComponent: PageComponentType): void => {
  pageComponent.getLayout = function getLayout(page: ReactElement) {
    return <DashboardLayout>{page}</DashboardLayout>;
  };
};

ব্যাস, কাজ শেষ। এখন আমি শুধু মাত্র উক্ত Page কে Argument হিসেবে দিয়ে Specific utility Function কে call দিব। যেমন,

HomePage এর ক্ষেত্রে,

import React from 'react';
import RootLayout from '@/components/layouts/RootLayout';
import { setRootLayout } from "@/utils/layout";
const HomePage = () => {
  return (
    <div>
      Home page
    </div>
  );
};

export default HomePage;
// setRootLayout function use করার মানে হলো আমি এই পেইজের জন্য RootLayout কে ইউজ করছি
setRootLayout(HomePage)

একইভাবে, dashboard.tsx পেইজের জন্যঃ

import React from "react";
import DashboardLayout from "@/components/layouts/DashboardLayout";
import { setDashboardLayout } from "@/utils/layout";

const Dashboard = () => {
  return <div>Dashboard Page</div>;
};

export default Dashboard;
// setDashboardLayout function use করার মানে হলো আমি এই পেইজের জন্য DashboardLayout কে ইউজ করছি
setDashboardLayout(Dashboard)

আশা করছি Next JS এ কিভাবে Page Router ব্যবহার করে Layout ইউজ করতে হয় সেটি সম্পর্কে আপনাদের একটা সচ্ছ ধারনা হয়েছে।

সবাইকে অসংখ্য ধন্যবাদ এতক্ষন ধরে সাথে থাকার জন্য।

Hello, This is Dipta Saha. I am also learning web development. If you found any wrong information from this blog, then please let me know. I appreciate your comments, and I am open to making corrections to ensure the accuracy of the content. If you found this blog helpful, I kindly request you to like and follow me for more interesting blogs in the future. Your support motivates me to continue sharing valuable insights and knowledge about web development. Thank you!