Gérer différents layouts persistants avec NextJS et Typescript

author
Andréas Hanss · Jan 5, 2022
dev | 2 min
Article image

Lorsque l'on développe une application NextJS il est fréquent de vouloir définir des mises en pages communes entre nos différentes pages.

Se pose alors le problème de la réutilisation. La documentation officielle nous propose une approche certes pratique mais un peu sommaire de comment gérer ses mises en page.

Une méthode simple pour gérer ses gabarits

Elle se décompose en 3 choses :

  1. Un composant LayoutFactory qui permet à partir d'une propriété de venir charger dynamiquement un layout donné.
  2. Un fichier de types qui permet de définir un contrat d'interface entre nos page et nos mises en page
  3. Des composants de layout respectant ce contrat d'interface
  4. Et enfin une mise à jour dans le fichier pages/_app.tsx pour prendre en compte notre layout

Commençons par le fichier de définitions de types, ici on a CodingSparkAppProps qui sont nos propriétés qui seront utilisées dans pages/_app.tsx puis on définit CodingSparkPageLayoutProps qui surcharge un type assez générique LayoutArgs avec les layouts qui sont disponibles pour notre application.

On étends bien évidemment les types par défaut de NextJS : NextPage.

1
import type { NotAuthenticatedLayoutArgs } from "modules/ui/layouts/NotAuthenticatedLayout";
2
import type { OnboardingLayoutArgs } from "modules/ui/layouts/OnboardingLayout";
3
import type { NextPage } from "next";
4
import type { AppProps } from "next/app";
5
interface LayoutArgs {
6
layout?: { type: string };
7
}
8
export interface CodingSparkPageLayoutProps extends LayoutArgs {
9
layout?: NotAuthenticatedLayoutArgs | OnboardingLayoutArgs | { type: "none" };
10
}
11
12
export type CodingSparkNextPage<P = Record<string, never>, IP = P> = NextPage<P, IP> & CodingSparkPageLayoutProps;
13
14
export type CodingSparkAppProps = AppProps & {
15
Component: CodingSparkNextPage;
16
};

Dans le fichier qui suit on notera que par défaut on gère un layout « none » ce qui signifie qu'il n'y a pas de mise en page. C'est utile dans le cas où vous n'avez pas encore migré l'intégralité de vos pages vers ce système de layout.

Ici car on en a peu, on part sur un switch/case mais on pourrait bien utiliser une table de correspondance comme une Map ou un objet. C'est d'ailleurs ce que je vous invite à faire si vous commencez à avoir beaucoup de layouts.

À noter qu'on utilise next/dynamic pour ne charger les layouts que lorsqu'ils sont utilisés afin de d'optimiser les premiers chargements de l'application avec du chunking.

1
import type { CodingSparkPageLayoutProps } from "modules/nextjs";
2
import dynamic from "next/dynamic";
3
4
const NotAuthenticatedLayout = dynamic(() => import("modules/ui/layouts/NotAuthenticatedLayout"));
5
const OnboardingLayout = dynamic(() => import("modules/ui/layouts/OnboardingLayout"));
6
7
export const LayoutFactory: React.FC<{ layout: CodingSparkPageLayoutProps["layout"] }> = ({
8
layout = { type: "none" },
9
children,
10
}) => {
11
switch (layout.type) {
12
case "not_authenticated":
13
return <NotAuthenticatedLayout>{children}</NotAuthenticatedLayout>;
14
case "onboarding":
15
return <OnboardingLayout>{children}</OnboardingLayout>;
16
default:
17
return <>{children}</>;
18
}
19
};

Enfin très simplement on va aller « Taguer » notre page avec les meta-données que l'ont a définies plus haut dans le fichier d'interfaces.

And voilà, rien de bien compliqué on profite même de l'auto-complétion !

1
import type { CodingSparkNextPage } from "modules/nextjs";
2
3
const OnboardingPage: CodingSparkNextPage = () => {
4
return <div>Some content</div>;
5
};
6
7
OnboardingPage.layout = { type: "onboarding" };
8
9
export default OnboardingPage;

La dernière chose qu'il nous reste à faire et brancher nos layouts avec notre application, pour se faire on procède de la sorte dans le fichier page/_app.tsx.

1
import "tailwindcss/tailwind.css";
2
3
import type { CodingSparkAppProps } from "modules/nextjs";
4
import { LayoutFactory } from "modules/ui/layouts/LayoutFactory";
5
import { DefaultSeo } from "next-seo";
6
import React from "react";
7
8
function MyApp({ Component, pageProps }: CodingSparkAppProps) {
9
const { layout } = Component;
10
11
return (
12
<>
13
<DefaultSeo
14
titleTemplate="%s | CodingSpark.io"
15
description="L'écosystème par les développeurs et pour les développeurs souhaitant créer des application modernes et performantes."
16
/>
17
<LayoutFactory layout={layout}>
18
<Component {...pageProps} />
19
</LayoutFactory>
20
</>
21
);
22
}
23
24
export default MyApp;

Il ne reste plus qu'à savourer le doux parfum de la réussite… 😌