Vitrine
Site vitrine pour prĂ©senter une activitĂ© : services, Ă propos, contact. Pas de compte utilisateur, pas de base de donnĂ©es â le strict nĂ©cessaire.
RĂ©utilisable vs spĂ©cifique : les Ă©tapes rĂ©utilisable sont valables pour n'importe quel projet de ta stack. Les Ă©tapes spĂ©cifique sont propres Ă un site vitrine â adapte le contenu (pages, textes) au tien.
Initialiser le projet Next.js
Un projet Next.js qui dĂ©marre en local, avec Tailwind et shadcn/ui prĂȘts Ă l'emploi.
Prérequis : Node.js installé. Terminal ouvert dans le dossier du projet.
-
Installe pnpm si besoin
npm install -g pnpm
pnpm est le gestionnaire de paquets choisi pour ce projet â plus rapide et plus Ă©conome en espace disque que npm.đ Comment voir le rĂ©sultat : tapepnpm -v. Un numĂ©ro de version s'affiche (ex.9.12.0) â sinon l'installation a Ă©chouĂ©. -
Crée le projet Next.js
pnpm create next-app@latest . --typescript --tailwind --eslint --app --src-dir=false --import-alias "@/*"
Le nom du dossier ne doit pas contenir de majuscules (contrainte npm). S'il en contient, crĂ©e le projet dans un dossier temporaire au nom valide puis dĂ©place tout son contenu dans ton dossier de travail.đ Comment voir le rĂ©sultat : tapels. Tu dois voir apparaĂźtreapp/,package.json,tailwind.config.tsâ la commande a rempli ton dossier de fichiers. -
Initialise shadcn/ui
pnpm dlx shadcn@latest init
Ăa crĂ©ecomponents.jsonetlib/utils.ts, et prĂ©pare Tailwind pour recevoir des composants shadcn/ui prĂȘts Ă copier.đ Comment voir le rĂ©sultat : tapels components.json lib/utils.tsâ les deux fichiers doivent exister, sans message d'erreur « No such file ». -
Crée l'arborescence du projet
mkdir -p lib/auth lib/db lib/validations prisma testsmkdir lib/auth, lib/db, lib/validations, prisma, tests
On sĂ©pare dĂšs le dĂ©part : authentification, accĂšs base de donnĂ©es, schĂ©mas de validation. Ăa Ă©vite le fichier fourre-tout de 2000 lignes plus tard.đ Comment voir le rĂ©sultat : tapels libâ les dossiersauth,db,validationsdoivent apparaĂźtre. -
Initialise Git et fais le premier commit
git init git add -A git commit -m "chore: init projet Next.js"
đ Comment voir le rĂ©sultat : tapegit log --onelineâ une ligne avec ton message de commit doit s'afficher. -
Lance le serveur et vérifie
pnpm dev
Ouvrehttp://localhost:3000. Si tu as une erreur 500 mentionnant un module introuvable (ex.Can't resolve 'xxx') alors qu'il est bien danspackage.json: supprimenode_modules,pnpm-lock.yamlet.next, relancepnpm installpuispnpm dev. C'est presque toujours une installation incomplĂšte, pas un bug de code.đ Comment voir le rĂ©sultat : dans ton navigateur, ouvrelocalhost:3000. Tu dois voir la page d'accueil par dĂ©faut de Next.js (logo, liens « Docs », « Deploy »). Le terminal afficheReadyet n'affiche pas d'erreur rouge.
Structurer les pages
Une arborescence claire : Accueil, Services, Ă propos, Contact.
Prérequis : étape 1 validée.
-
Liste les pages nĂ©cessaires avant de coderĂvite d'improviser l'arborescence au fil de l'eau â note juste les 3-5 pages dont le site a vraiment besoin. Pour ce guide : Accueil, Services, Ă propos, Contact.
-
Crée les dossiers des 3 pages restantes
mkdir -p app/services app/a-propos app/contactmkdir app/services, app/a-propos, app/contact
app/page.tsx(la page d'accueil) existe dĂ©jĂ depuis la crĂ©ation du projet Ă l'Ă©tape 1 â seules les 3 nouvelles sections ont besoin d'un dossier.đ Comment voir le rĂ©sultat : tapels appâ les dossiersservices,a-proposetcontactdoivent apparaĂźtre. -
Crée le fichier
page.tsxde chaque pagetouch app/services/page.tsx app/a-propos/page.tsx app/contact/page.tsxNew-Item -ItemType File -Force -Path app/services/page.tsx, app/a-propos/page.tsx, app/contact/page.tsx
Dans le dossierapp/de Next.js, chaque sous-dossier contenant un fichierpage.tsxdevient une URL du mĂȘme nom.đ Comment voir le rĂ©sultat : tapels app/services app/a-propos app/contactâ chaque dossier doit contenir un fichierpage.tsx(vide pour l'instant, il ne s'affichera pas encore). -
Ouvre chaque fichier et colle un contenu minimal
// app/services/page.tsx export default function ServicesPage() { return ( <main className="p-8"> <h1 className="text-2xl font-bold">Nos services</h1> <p>DĂ©cris ici ce que tu proposes.</p> </main> ); }Un fichierpage.tsxvide fait planter le build (« The default export is not a React Component ») â il lui faut au minimum une fonction exportĂ©e par dĂ©faut qui retourne du JSX. RĂ©pĂšte la mĂȘme structure dansapp/a-propos/page.tsx(fonctionAProposPage) etapp/contact/page.tsx(fonctionContactPageâ le vrai formulaire arrive Ă l'Ă©tape 3, un simple titre suffit pour l'instant).đ Comment voir le rĂ©sultat : ouvrelocalhost:3000/services,/a-proposet/contactdans le navigateur. Chaque URL doit afficher son contenu â pas de page « 404 This page could not be found » ni d'erreur rouge dans le terminal. -
Crée le dossier et les fichiers du header et du footer
mkdir -p components && touch components/header.tsx components/footer.tsxNew-Item -ItemType Directory -Force -Path components; New-Item -ItemType File -Force -Path components/header.tsx, components/footer.tsx
đ Comment voir le rĂ©sultat : tapels componentsâheader.tsxetfooter.tsxdoivent apparaĂźtre. -
Colle le contenu du header et du footer
// components/header.tsx import Link from "next/link"; export default function Header() { return ( <header className="border-b p-4 flex gap-4"> <Link href="/">Accueil</Link> <Link href="/services">Services</Link> <Link href="/a-propos">Ă propos</Link> <Link href="/contact">Contact</Link> </header> ); }// components/footer.tsx export default function Footer() { return ( <footer className="border-t p-4 text-sm text-center"> © {new Date().getFullYear()} Ton ActivitĂ© </footer> ); }Ces deux composants seront importĂ©s une seule fois dansapp/layout.tsxĂ l'Ă©tape suivante â Ă©vite de dupliquer la navigation sur chaque page. -
Importe le header et le footer dans
app/layout.tsx// app/layout.tsx import Header from "@/components/header"; import Footer from "@/components/footer"; // ... dans le return, à l'intérieur de <body> : <body> <Header /> {children} <Footer /> </body>
Next.js appliqueapp/layout.tsxĂ toutes les pages â c'est le seul endroit oĂč dĂ©clarer le header et le footer.đ Comment voir le rĂ©sultat : retourne surlocalhost:3000â ton en-tĂȘte et ton pied de page doivent apparaĂźtre, et rester identiques si tu changes de page. -
Ajoute les composants shadcn/ui nécessaires
pnpm dlx shadcn@latest add button card
đ Comment voir le rĂ©sultat : tapels components/uiâbutton.tsxetcard.tsxdoivent apparaĂźtre, prĂȘts Ă ĂȘtre importĂ©s dans tes pages.
Habillage â Tailwind & shadcn/ui
Un rendu visuel cohérent sur tout le site, et une méthode simple pour le modifier plus tard.
Prérequis : étape 2 validée.
-
Comprends oĂč vit le styleIl n'y a pas un fichier
.csspar page. Deux niveaux seulement : les variables de thĂšme globales dansapp/globals.css(couleurs, arrondi â gĂ©nĂ©rĂ©es parshadcn initĂ l'Ă©tape 1, chargĂ©es une seule fois parapp/layout.tsx), et les classes Tailwind Ă©crites directement dans leclassNamede chaque composant. -
Personnalise les couleurs du thĂšmeOuvre
app/globals.cssâ repĂšre le bloc:root { ... }gĂ©nĂ©rĂ© parshadcn init, qui contient des variables comme--primaryou--background. Change leurs valeurs directement dans ce fichier.đ Comment voir le rĂ©sultat : modifie la valeur de--primary, enregistre, rechargelocalhost:3000â n'importe quel composant shadcn/ui (leButtonajoutĂ© Ă l'Ă©tape prĂ©cĂ©dente) doit changer de couleur automatiquement, sans toucher au code des pages. -
Crée une mise en page cohérente, réutilisée sur chaque page
touch components/container.tsxNew-Item -ItemType File -Force -Path components/container.tsx
// components/container.tsx export default function Container({ children }: { children: React.ReactNode }) { return ( <div className="max-w-5xl mx-auto px-4 py-12"> {children} </div> ); }Sans ça, tu rĂ©pĂštes les mĂȘmes classes d'espacement (max-w-5xl mx-auto px-4 py-12) dans chaque fichierpage.tsxâ un seul endroit Ă modifier si tu changes la largeur ou les marges du site.đ Comment voir le rĂ©sultat : enveloppe le contenu retournĂ© parapp/page.tsx,app/services/page.tsx, etc. avec<Container>...</Container>â chaque page doit s'afficher centrĂ©e, avec la mĂȘme marge, une fois rechargĂ©e. -
Remplace le HTML brut par les composants shadcn/ui déjà installés
// avant <button className="bg-blue-500 text-white px-4 py-2 rounded">Envoyer</button> // aprĂšs import { Button } from "@/components/ui/button"; <Button>Envoyer</Button>
Le composantButtonhĂ©rite automatiquement des variables de thĂšme dĂ©finies plus haut â un<button>HTML brut, non. Change--primaryet seul le premier ne suivra pas. -
Pour modifier le style plus tardGarde
pnpm devlancĂ© en continu â le rechargement Ă chaud applique tes changements dans le navigateur dĂšs que tu sauvegardes, sans redĂ©marrer le serveur. Ouvre le fichier de la page ou du composant concernĂ©, change les classes dansclassName, enregistre. Pour ajouter un nouveau composant shadcn/ui :pnpm dlx shadcn@latest add <nom>(liste sur ui.shadcn.com). Pour savoir ce que fait une classe (p-4,rounded-lg,text-xl...), consulte le lexique ou tailwindcss.com/docs.
Formulaire de contact
Recevoir un message par email, sans base de données à gérer.
Prérequis : étape 3 validée.
-
Choisis un service d'envoi d'emailPas besoin de stocker le message en base pour un simple formulaire de contact â un envoi direct par email suffit. Resend a un plan gratuit largement suffisant pour dĂ©marrer.
-
Installe le SDK
pnpm add resend
đ Comment voir le rĂ©sultat : ouvrepackage.jsonâresenddoit apparaĂźtre dansdependencies. -
Ajoute la clé API dans
.env# .env RESEND_API_KEY="ta-clé"
đ Comment voir le rĂ©sultat : pas d'effet visible tout de suite â cette clĂ© sera testĂ©e concrĂštement Ă l'Ă©tape suivante, quand le formulaire enverra un vrai email. -
Ăcris la Server Action d'envoi
"use server"; import { Resend } from "resend"; const resend = new Resend(process.env.RESEND_API_KEY); export async function sendContact(formData: FormData) { const email = formData.get("email"); const message = formData.get("message"); await resend.emails.send({ from: "site@tondomaine.com", to: "toi@tondomaine.com", subject: "Nouveau message du site", text: `${email}\n\n${message}`, }); }Valideemailetmessageavec Zod avant l'envoi â bloque les champs vides ou les emails mal formĂ©s.đ Comment voir le rĂ©sultat : remplis le formulaire surlocalhost:3000/contactavec un vrai email et envoie-le. VĂ©rifie ta boĂźte de rĂ©ception (et les spams) â le message doit arriver en quelques secondes.
SEO de base
Que le site soit correctement référencé et compris par Google.
Prérequis : étape 4 validée.
-
Renseigne les métadonnées
// app/layout.tsx export const metadata = { title: "Nom de ton activitĂ©", description: "Une phrase claire sur ce que tu proposes.", };C'est ce texte qui s'affiche dans l'onglet du navigateur et dans les rĂ©sultats Google.đ Comment voir le rĂ©sultat : rechargelocalhost:3000et regarde l'onglet du navigateur â le nouveau titre doit y ĂȘtre affichĂ© Ă la place de « create-next-app ». -
Crée un sitemap et un robots.txt
touch app/sitemap.ts app/robots.tsNew-Item -ItemType File -Force -Path app/sitemap.ts, app/robots.ts
Deux fichiers spéciaux Next.js qui indiquent à Google quelles pages explorer. -
Colle le contenu de chaque fichier
// app/sitemap.ts import { MetadataRoute } from "next"; export default function sitemap(): MetadataRoute.Sitemap { const base = "https://tondomaine.vercel.app"; return ["", "/services", "/a-propos", "/contact"].map((path) => ({ url: `${base}${path}`, lastModified: new Date(), })); }// app/robots.ts import { MetadataRoute } from "next"; export default function robots(): MetadataRoute.Robots { return { rules: { userAgent: "*", allow: "/" }, sitemap: "https://tondomaine.vercel.app/sitemap.xml", }; }Remplacetondomaine.vercel.apppar ton URL rĂ©elle une fois qu'elle est connue (Ă©tape 5).đ Comment voir le rĂ©sultat : ouvrelocalhost:3000/sitemap.xmletlocalhost:3000/robots.txtâ chacune doit afficher du texte structurĂ©, pas une page d'erreur. -
Utilise
next/imagepour toutes les imagesCompression et chargement diffĂ©rĂ© automatiques â un site plus rapide se classe mieux.đ Comment voir le rĂ©sultat : clic droit sur une image affichĂ©e â « Inspecter » â la balise doit avoir un attributsrcsetgĂ©nĂ©rĂ© automatiquement, avec plusieurs tailles d'image.
DĂ©ploiement â Vercel
Une URL publique et fonctionnelle.
Prérequis : étape 5 validée.
-
Pousse le code sur GitHub
git remote add origin URL-DE-TON-DEPOT git push -u origin masterđ Comment voir le rĂ©sultat : rafraĂźchis la page du dĂ©pĂŽt sur GitHub dans ton navigateur â tes fichiers doivent y apparaĂźtre. -
Importe le projet sur VercelSur vercel.com â Add New â Project â sĂ©lectionne le dĂ©pĂŽt GitHub.đ Comment voir le rĂ©sultat : le tableau de bord Vercel passe de « Building » à « Ready » (quelques dizaines de secondes), avec un lien de prĂ©visualisation cliquable.
-
Ajoute
RESEND_API_KEYdans les variables d'environnementSettings â Environment Variables â sans ça le formulaire de contact ne fonctionnera pas en production.đ Comment voir le rĂ©sultat : pas d'effet visible immĂ©diatement â redĂ©ploie (bouton « Redeploy ») puis teste le formulaire en production Ă l'Ă©tape suivante. -
DĂ©ploie et vĂ©rifie en ligneOuvre l'URL fournie par Vercel, teste le formulaire de contact pour de vrai.đ Comment voir le rĂ©sultat : le site s'affiche sur l'URL
*.vercel.app, et un message envoyĂ© depuis ce formulaire en ligne arrive bien dans ta boĂźte mail â comme en local, mais pour de vrai.